diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-10-24 14:44:48 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-10-24 14:44:48 -0400 |
commit | 6daa52b3b93328506e34d6595a01d30c334897bc (patch) | |
tree | bdbe951d06f95a1a7752d5c23950eda7bb36a9e7 /indra | |
parent | 850bdf4ce335049a75e1c4f6400b627bf328a264 (diff) | |
parent | b611324db58def7d65f4bfc721e7dc1c378b7fa3 (diff) |
Merge branch 'develop' into maxim/viewer_2245
Diffstat (limited to 'indra')
93 files changed, 962 insertions, 717 deletions
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 3be8ebafa8..62ed631e06 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -29,6 +29,9 @@ else() set( USE_AUTOBUILD_3P ON ) endif() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + include(Variables) include(BuildVersion) diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h index 5e785e4f3e..116eb3bee0 100644 --- a/indra/llappearance/lltexlayerparams.h +++ b/indra/llappearance/lltexlayerparams.h @@ -30,6 +30,7 @@ #include "llpointer.h" #include "v4color.h" #include "llviewervisualparam.h" +#include <atomic> class LLAvatarAppearance; class LLImageRaw; diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 78bfaade55..aa8810f00b 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -70,6 +70,7 @@ set(llcommon_SOURCE_FILES llmetrics.cpp llmortician.cpp llmutex.cpp + llpointer.cpp llpredicate.cpp llprocess.cpp llprocessor.cpp diff --git a/indra/llcommon/coro_scheduler.cpp b/indra/llcommon/coro_scheduler.cpp index 2d8b6e1a97..b6117fa6a1 100644 --- a/indra/llcommon/coro_scheduler.cpp +++ b/indra/llcommon/coro_scheduler.cpp @@ -20,6 +20,7 @@ #include <boost/fiber/operations.hpp> // other Linden headers #include "llcallbacklist.h" +#include "llcoros.h" #include "lldate.h" #include "llerror.h" @@ -56,17 +57,55 @@ void scheduler::awakened( boost::fibers::context* ctx) noexcept boost::fibers::context* scheduler::pick_next() noexcept { + auto now = LLDate::now().secondsSinceEpoch(); // count calls to pick_next() ++mSwitches; // pick_next() is called when the previous fiber has suspended, and we // need to pick another. Did the previous pick_next() call pick the main - // fiber? If so, it's the main fiber that just suspended. - auto now = LLDate::now().secondsSinceEpoch(); - if (mMainRunning) + // fiber? (Or is this the first pick_next() call?) If so, it's the main + // fiber that just suspended. + if ((! mPrevCtx) || mPrevCtx->get_id() == mMainID) { - mMainRunning = false; mMainLast = now; } + else + { + // How long did we spend in the fiber that just suspended? + // Don't bother with long runs of the main fiber, since (a) it happens + // pretty often and (b) it's moderately likely that we've reached here + // from the canonical yield at the top of mainloop, and what we'd want + // to know about is whatever the main fiber was doing in the + // *previous* iteration of mainloop. + F64 elapsed{ now - mResumeTime }; + LLCoros::CoroData& data{ LLCoros::get_CoroData(mPrevCtx->get_id()) }; + // Find iterator to the first mHistogram key greater than elapsed. + auto past = data.mHistogram.upper_bound(elapsed); + // If the smallest key (mHistogram.begin()->first) is greater than + // elapsed, then we need not bother with this timeslice. + if (past != data.mHistogram.begin()) + { + // Here elapsed was greater than at least one key. Back off to the + // previous entry and increment that count. If it's end(), backing + // off gets us the last entry -- assuming mHistogram isn't empty. + llassert(! data.mHistogram.empty()); + ++(--past)->second; + LL::WorkQueue::ptr_t queue{ getWorkQueue() }; + // make sure the queue exists + if (queue) + { + // If it proves difficult to track down *why* the fiber spent so + // much time, consider also binding and reporting + // boost::stacktrace::stacktrace(). + queue->post( + [name=data.getName(), elapsed] + { + LL_WARNS_ONCE("LLCoros.scheduler") + << "Coroutine " << name << " ran for " + << elapsed << " seconds" << LL_ENDL; + }); + } + } + } boost::fibers::context* next; @@ -96,17 +135,9 @@ boost::fibers::context* scheduler::pick_next() noexcept // passage could be skipped. // Record this event for logging, but push it off to a thread pool to - // perform that work. Presumably std::weak_ptr::lock() is cheaper than - // WorkQueue::getInstance(). - LL::WorkQueue::ptr_t queue{ mQueue.lock() }; - // We probably started before the relevant WorkQueue was created. - if (! queue) - { - // Try again to locate the specified WorkQueue. - queue = LL::WorkQueue::getInstance(qname); - mQueue = queue; - } - // Both the lock() call and the getInstance() call might have failed. + // perform that work. + LL::WorkQueue::ptr_t queue{ getWorkQueue() }; + // The work queue we're looking for might not exist right now. if (queue) { // Bind values. Do NOT bind 'this' to avoid cross-thread access! @@ -116,7 +147,6 @@ boost::fibers::context* scheduler::pick_next() noexcept // so we have no access. queue->post( [switches=mSwitches, start=mStart, elapsed, now] - () { U32 runtime(U32(now) - U32(start)); U32 minutes(runtime / 60u); @@ -150,12 +180,29 @@ boost::fibers::context* scheduler::pick_next() noexcept { // we're about to resume the main fiber: it's no longer "ready" mMainCtx = nullptr; - // instead, it's "running" - mMainRunning = true; } + mPrevCtx = next; + // remember when we resumed this fiber so our next call can measure how + // long the previous resumption was + mResumeTime = LLDate::now().secondsSinceEpoch(); return next; } +LL::WorkQueue::ptr_t scheduler::getWorkQueue() +{ + // Cache a weak_ptr to our target work queue, presuming that + // std::weak_ptr::lock() is cheaper than WorkQueue::getInstance(). + LL::WorkQueue::ptr_t queue{ mQueue.lock() }; + // We probably started before the relevant WorkQueue was created. + if (! queue) + { + // Try again to locate the specified WorkQueue. + queue = LL::WorkQueue::getInstance(qname); + mQueue = queue; + } + return queue; +} + void scheduler::use() { boost::fibers::use_scheduling_algorithm<scheduler>(); diff --git a/indra/llcommon/coro_scheduler.h b/indra/llcommon/coro_scheduler.h index eee2d746b5..7af90685dc 100644 --- a/indra/llcommon/coro_scheduler.h +++ b/indra/llcommon/coro_scheduler.h @@ -47,17 +47,20 @@ public: static void use(); private: - // This is the fiber::id of the main fiber. We use this to discover - // whether the fiber passed to awakened() is in fact the main fiber. + LL::WorkQueue::ptr_t getWorkQueue(); + + // This is the fiber::id of the main fiber. boost::fibers::fiber::id mMainID; - // This context* is nullptr until awakened() notices that the main fiber - // has become ready, at which point it contains the main fiber's context*. + // This context* is nullptr while the main fiber is running or suspended, + // but is set to the main fiber's context each time the main fiber is ready. boost::fibers::context* mMainCtx{}; - // Set when pick_next() returns the main fiber. - bool mMainRunning{ false }; + // Remember the context returned by the previous pick_next() call. + boost::fibers::context* mPrevCtx{}; // If it's been at least this long since the last time the main fiber got // control, jump it to the head of the queue. F64 mTimeslice{ DEFAULT_TIMESLICE }; + // Time when we resumed the most recently running fiber + F64 mResumeTime{ 0 }; // Timestamp as of the last time we suspended the main fiber. F64 mMainLast{ 0 }; // Timestamp of start time diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h index 1b4aec09b4..f66970ed8f 100644 --- a/indra/llcommon/fsyspath.h +++ b/indra/llcommon/fsyspath.h @@ -12,7 +12,10 @@ #if ! defined(LL_FSYSPATH_H) #define LL_FSYSPATH_H +#include <boost/iterator/transform_iterator.hpp> #include <filesystem> +#include <string> +#include <string_view> // While std::filesystem::path can be directly constructed from std::string on // both Posix and Windows, that's not what we want on Windows. Per @@ -33,42 +36,55 @@ // char"), the "native narrow encoding" isn't UTF-8, so file paths containing // non-ASCII characters get mangled. // -// Once we're building with C++20, we could pass a UTF-8 std::string through a -// vector<char8_t> to engage std::filesystem::path's own UTF-8 conversion. But -// sigh, as of 2024-04-03 we're not yet there. -// -// Anyway, encapsulating the important UTF-8 conversions in our own subclass -// allows us to migrate forward to C++20 conventions without changing -// referencing code. +// Encapsulating the important UTF-8 conversions in our own subclass allows us +// to migrate forward to C++20 conventions without changing referencing code. class fsyspath: public std::filesystem::path { using super = std::filesystem::path; + // In C++20 (__cpp_lib_char8_t), std::filesystem::u8path() is deprecated. + // std::filesystem::path(iter, iter) performs UTF-8 conversions when the + // value_type of the iterators is char8_t. While we could copy into a + // temporary std::u8string and from there into std::filesystem::path, to + // minimize string copying we'll define a transform_iterator that accepts + // a std::string_view::iterator and dereferences to char8_t. + struct u8ify + { + char8_t operator()(char c) const { return char8_t(c); } + }; + using u8iter = boost::transform_iterator<u8ify, std::string_view::iterator>; + public: // default fsyspath() {} - // construct from UTF-8 encoded std::string - fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {} - // construct from UTF-8 encoded const char* - fsyspath(const char* path): super(std::filesystem::u8path(path)) {} + // construct from UTF-8 encoded string + fsyspath(const std::string& path): fsyspath(std::string_view(path)) {} + fsyspath(const char* path): fsyspath(std::string_view(path)) {} + fsyspath(std::string_view path): + super(u8iter(path.begin(), u8ify()), u8iter(path.end(), u8ify())) + {} // construct from existing path fsyspath(const super& path): super(path) {} - fsyspath& operator=(const super& p) { super::operator=(p); return *this; } - fsyspath& operator=(const std::string& p) - { - super::operator=(std::filesystem::u8path(p)); - return *this; - } - fsyspath& operator=(const char* p) + fsyspath& operator=(const super& p) { super::operator=(p); return *this; } + fsyspath& operator=(const std::string& p) { return (*this) = std::string_view(p); } + fsyspath& operator=(const char* p) { return (*this) = std::string_view(p); } + fsyspath& operator=(std::string_view p) { - super::operator=(std::filesystem::u8path(p)); + assign(u8iter(p.begin(), u8ify()), u8iter(p.end(), u8ify())); return *this; } // shadow base-class string() method with UTF-8 aware method - std::string string() const { return super::u8string(); } + std::string string() const + { + // Short of forbidden type punning, I see no way to avoid copying this + // std::u8string to a std::string. + auto u8str{ super::u8string() }; + // from https://github.com/tahonermann/char8_t-remediation/blob/master/char8_t-remediation.h#L180-L182 + return { u8str.begin(), u8str.end() }; + } // On Posix systems, where value_type is already char, this operator // std::string() method shadows the base class operator string_type() // method. But on Windows, where value_type is wchar_t, the base class diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 1ae5c87a00..5a3cbd2ef1 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -57,16 +57,19 @@ #include "llsdutil.h" #include "lltimer.h" #include "stringize.h" +#include "scope_exit.h" #if LL_WINDOWS #include <excpt.h> #endif +thread_local std::unordered_map<std::string, int> LLCoros::mPrefixMap; +thread_local std::unordered_map<std::string, LLCoros::id> LLCoros::mNameMap; + // static bool LLCoros::on_main_coro() { - return (!LLCoros::instanceExists() || - LLCoros::getName().empty()); + return (!instanceExists() || get_CoroData().isMain); } // static @@ -76,11 +79,11 @@ bool LLCoros::on_main_thread_main_coro() } // static -LLCoros::CoroData& LLCoros::get_CoroData(const std::string&) +LLCoros::CoroData& LLCoros::get_CoroData() { CoroData* current{ nullptr }; // be careful about attempted accesses in the final throes of app shutdown - if (! wasDeleted()) + if (instanceExists()) { current = instance().mCurrent.get(); } @@ -89,16 +92,26 @@ LLCoros::CoroData& LLCoros::get_CoroData(const std::string&) // canonical values. if (! current) { - static std::atomic<int> which_thread(0); - // Use alternate CoroData constructor. - static thread_local CoroData sMain(which_thread++); // We need not reset() the local_ptr to this instance; we'll simply // find it again every time we discover that current is null. - current = &sMain; + current = &main_CoroData(); } return *current; } +LLCoros::CoroData& LLCoros::get_CoroData(id id) +{ + auto found = CoroData::getInstance(id); + return found? *found : main_CoroData(); +} + +LLCoros::CoroData& LLCoros::main_CoroData() +{ + // tell CoroData we're "main" + static thread_local CoroData sMain(""); + return sMain; +} + //static LLCoros::coro::id LLCoros::get_self() { @@ -108,28 +121,28 @@ LLCoros::coro::id LLCoros::get_self() //static void LLCoros::set_consuming(bool consuming) { - auto& data(get_CoroData("set_consuming()")); + auto& data(get_CoroData()); // DO NOT call this on the main() coroutine. - llassert_always(! data.mName.empty()); + llassert_always(! data.isMain); data.mConsuming = consuming; } //static bool LLCoros::get_consuming() { - return get_CoroData("get_consuming()").mConsuming; + return get_CoroData().mConsuming; } // static void LLCoros::setStatus(const std::string& status) { - get_CoroData("setStatus()").mStatus = status; + get_CoroData().mStatus = status; } // static std::string LLCoros::getStatus() { - return get_CoroData("getStatus()").mStatus; + return get_CoroData().mStatus; } LLCoros::LLCoros(): @@ -186,9 +199,8 @@ void LLCoros::cleanupSingleton() std::string LLCoros::generateDistinctName(const std::string& prefix) const { - static int unique = 0; - - // Allowing empty name would make getName()'s not-found return ambiguous. + // Empty name would trigger CoroData's constructor's special case for the + // main coroutine. if (prefix.empty()) { LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL; @@ -196,9 +208,11 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const // If the specified name isn't already in the map, just use that. std::string name(prefix); + // maintain a distinct int suffix for each prefix + int& unique = mPrefixMap[prefix]; - // Until we find an unused name, append a numeric suffix for uniqueness. - while (CoroData::getInstance(name)) + // Until we find an unused name, append int suffix for uniqueness. + while (mNameMap.find(name) != mNameMap.end()) { name = stringize(prefix, unique++); } @@ -207,9 +221,16 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const bool LLCoros::killreq(const std::string& name) { - auto found = CoroData::getInstance(name); + auto foundName = mNameMap.find(name); + if (foundName == mNameMap.end()) + { + // couldn't find that name in map + return false; + } + auto found = CoroData::getInstance(foundName->second); if (! found) { + // found name, but CoroData with that ID key no longer exists return false; } // Next time the subject coroutine calls checkStop(), make it terminate. @@ -224,14 +245,13 @@ bool LLCoros::killreq(const std::string& name) //static std::string LLCoros::getName() { - return get_CoroData("getName()").mName; + return get_CoroData().getName(); } -//static -std::string LLCoros::logname() +// static +std::string LLCoros::getName(id id) { - auto& data(get_CoroData("logname()")); - return data.mName.empty()? data.getKey() : data.mName; + return get_CoroData(id).getName(); } void LLCoros::saveException(const std::string& name, std::exception_ptr exc) @@ -264,7 +284,7 @@ void LLCoros::printActiveCoroutines(const std::string& when) { LL_INFOS("LLCoros") << "-------------- List of active coroutines ------------"; F64 time = LLTimer::getTotalSeconds(); - for (auto& cd : CoroData::instance_snapshot()) + for (const auto& cd : CoroData::instance_snapshot()) { F64 life_time = time - cd.mCreationTime; LL_CONT << LL_NEWLINE @@ -323,6 +343,33 @@ void LLCoros::toplevel(std::string name, callable_t callable) // run the code the caller actually wants in the coroutine try { + LL::scope_exit report{ + [&corodata] + { + bool allzero = true; + for (const auto& [threshold, occurs] : corodata.mHistogram) + { + if (occurs) + { + allzero = false; + break; + } + } + if (! allzero) + { + LL_WARNS("LLCoros") << "coroutine " << corodata.mName; + const char* sep = " exceeded "; + for (const auto& [threshold, occurs] : corodata.mHistogram) + { + if (occurs) + { + LL_CONT << sep << threshold << " " << occurs << " times"; + sep = ", "; + } + } + LL_ENDL; + } + }}; LL::seh::catcher(callable); } catch (const Stop& exc) @@ -364,8 +411,8 @@ void LLCoros::checkStop(callable_t cleanup) // do this AFTER the check above, because get_CoroData() depends on the // local_ptr in our instance(). - auto& data(get_CoroData("checkStop()")); - if (data.mName.empty()) + auto& data(get_CoroData()); + if (data.isMain) { // Our Stop exception and its subclasses are intended to stop loitering // coroutines. Don't throw it from the main coroutine. @@ -385,7 +432,7 @@ void LLCoros::checkStop(callable_t cleanup) { // Someone wants to kill this coroutine cleanup(); - LLTHROW(Killed(stringize("coroutine ", data.mName, " killed by ", data.mKilledBy))); + LLTHROW(Killed(stringize("coroutine ", data.getName(), " killed by ", data.mKilledBy))); } } @@ -445,20 +492,51 @@ LLBoundListener LLCoros::getStopListener(const std::string& caller, } LLCoros::CoroData::CoroData(const std::string& name): - LLInstanceTracker<CoroData, std::string>(name), + super(boost::this_fiber::get_id()), mName(name), - mCreationTime(LLTimer::getTotalSeconds()) + mCreationTime(LLTimer::getTotalSeconds()), + // Preset threshold times in mHistogram + mHistogram{ + {0.004, 0}, + {0.040, 0}, + {0.400, 0}, + {1.000, 0} + } { + // we expect the empty string for the main coroutine + if (name.empty()) + { + isMain = true; + if (on_main_thread()) + { + // main coroutine on main thread + mName = "main"; + } + else + { + // main coroutine on some other thread + static std::atomic<int> main_no{ 0 }; + mName = stringize("main", ++main_no); + } + } + // maintain LLCoros::mNameMap + LLCoros::mNameMap.emplace(mName, getKey()); +} + +LLCoros::CoroData::~CoroData() +{ + // Don't try to erase the static main CoroData from our static + // thread_local mNameMap; that could run into destruction order problems. + if (! isMain) + { + LLCoros::mNameMap.erase(mName); + } } -LLCoros::CoroData::CoroData(int n): - // This constructor is used for the thread_local instance belonging to the - // default coroutine on each thread. We must give each one a different - // LLInstanceTracker key because LLInstanceTracker's map spans all - // threads, but we want the default coroutine on each thread to have the - // empty string as its visible name because some consumers test for that. - LLInstanceTracker<CoroData, std::string>("main" + stringize(n)), - mName(), - mCreationTime(LLTimer::getTotalSeconds()) +std::string LLCoros::CoroData::getName() const { + if (mStatus.empty()) + return mName; + else + return stringize(mName, " (", mStatus, ")"); } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 0291d7f1d9..1edcb7e387 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -37,41 +37,38 @@ #include <boost/fiber/fss.hpp> #include <exception> #include <functional> +#include <map> #include <queue> #include <string> +#include <unordered_map> + +namespace llcoro +{ +class scheduler; +} /** - * Registry of named Boost.Coroutine instances - * - * The Boost.Coroutine library supports the general case of a coroutine - * accepting arbitrary parameters and yielding multiple (sets of) results. For - * such use cases, it's natural for the invoking code to retain the coroutine - * instance: the consumer repeatedly calls into the coroutine, perhaps passing - * new parameter values, prompting it to yield its next result. - * - * Our typical coroutine usage is different, though. For us, coroutines - * provide an alternative to the @c Responder pattern. Our typical coroutine - * has @c void return, invoked in fire-and-forget mode: the handler for some - * user gesture launches the coroutine and promptly returns to the main loop. - * The coroutine initiates some action that will take multiple frames (e.g. a - * capability request), waits for its result, processes it and silently steals - * away. + * Registry of named Boost.Fiber instances * - * This usage poses two (related) problems: + * When the viewer first introduced the semi-independent execution agents now + * called fibers, the term "fiber" had not yet become current, and the only + * available libraries used the term "coroutine" instead. Within the viewer we + * continue to use the term "coroutines," though at present they are actually + * boost::fibers::fiber instances. * - * # Who should own the coroutine instance? If it's simply local to the - * handler code that launches it, return from the handler will destroy the - * coroutine object, terminating the coroutine. - * # Once the coroutine terminates, in whatever way, who's responsible for - * cleaning up the coroutine object? + * Coroutines provide an alternative to the @c Responder pattern. Our typical + * coroutine has @c void return, invoked in fire-and-forget mode: the handler + * for some user gesture launches the coroutine and promptly returns to the + * main loop. The coroutine initiates some action that will take multiple + * frames (e.g. a capability request), waits for its result, processes it and + * silently steals away. * * LLCoros is a Singleton collection of currently-active coroutine instances. * Each has a name. You ask LLCoros to launch a new coroutine with a suggested * name prefix; from your prefix it generates a distinct name, registers the * new coroutine and returns the actual name. * - * The name - * can provide diagnostic info: we can look up the name of the + * The name can provide diagnostic info: we can look up the name of the * currently-running coroutine. */ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> @@ -91,12 +88,8 @@ public: // llassert(LLCoros::on_main_thread_main_coro()) static bool on_main_thread_main_coro(); - /// The viewer's use of the term "coroutine" became deeply embedded before - /// the industry term "fiber" emerged to distinguish userland threads from - /// simpler, more transient kinds of coroutines. Semantically they've - /// always been fibers. But at this point in history, we're pretty much - /// stuck with the term "coroutine." typedef boost::fibers::fiber coro; + typedef coro::id id; /// Canonical callable type typedef std::function<void()> callable_t; @@ -150,13 +143,16 @@ public: /** * From within a coroutine, look up the (tweaked) name string by which - * this coroutine is registered. Returns the empty string if not found - * (e.g. if the coroutine was launched by hand rather than using - * LLCoros::launch()). + * this coroutine is registered. */ static std::string getName(); /** + * Given an id, return the name of that coroutine. + */ + static std::string getName(id); + + /** * rethrow() is called by the thread's main fiber to propagate an * exception from any coroutine into the main fiber, where it can engage * the normal unhandled-exception machinery, up to and including crash @@ -170,13 +166,6 @@ public: void rethrow(); /** - * This variation returns a name suitable for log messages: the explicit - * name for an explicitly-launched coroutine, or "mainN" for the default - * coroutine on a thread. - */ - static std::string logname(); - - /** * For delayed initialization. To be clear, this will only affect * coroutines launched @em after this point. The underlying facility * provides no way to alter the stack size of any running coroutine. @@ -187,7 +176,7 @@ public: void printActiveCoroutines(const std::string& when=std::string()); /// get the current coro::id for those who really really care - static coro::id get_self(); + static id get_self(); /** * Most coroutines, most of the time, don't "consume" the events for which @@ -236,6 +225,7 @@ public: setStatus(status); } TempStatus(const TempStatus&) = delete; + TempStatus& operator=(const TempStatus&) = delete; ~TempStatus() { setStatus(mOldStatus); @@ -331,10 +321,14 @@ public: using local_ptr = boost::fibers::fiber_specific_ptr<T>; private: + friend class llcoro::scheduler; + std::string generateDistinctName(const std::string& prefix) const; void toplevel(std::string name, callable_t callable); struct CoroData; - static CoroData& get_CoroData(const std::string& caller); + static CoroData& get_CoroData(); + static CoroData& get_CoroData(id); + static CoroData& main_CoroData(); void saveException(const std::string& name, std::exception_ptr exc); LLTempBoundListener mConn; @@ -355,13 +349,18 @@ private: S32 mStackSize; // coroutine-local storage, as it were: one per coro we track - struct CoroData: public LLInstanceTracker<CoroData, std::string> + struct CoroData: public LLInstanceTracker<CoroData, id> { + using super = LLInstanceTracker<CoroData, id>; + CoroData(const std::string& name); - CoroData(int n); + ~CoroData(); + std::string getName() const; + + bool isMain{ false }; // tweaked name of the current coroutine - const std::string mName; + std::string mName; // set_consuming() state -- don't consume events unless specifically directed bool mConsuming{ false }; // killed by which coroutine @@ -369,20 +368,24 @@ private: // setStatus() state std::string mStatus; F64 mCreationTime; // since epoch + // Histogram of how many times this coroutine's timeslice exceeds + // certain thresholds. mHistogram is pre-populated with those + // thresholds as keys. If k0 is one threshold key and k1 is the next, + // mHistogram[k0] is the number of times a coroutine timeslice tn ran + // (k0 <= tn < k1). A timeslice less than mHistogram.begin()->first is + // fine; we don't need to record those. + std::map<F64, U32> mHistogram; }; // Identify the current coroutine's CoroData. This local_ptr isn't static // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. local_ptr<CoroData> mCurrent; -}; -namespace llcoro -{ - -inline -std::string logname() { return LLCoros::logname(); } - -} // llcoro + // ensure name uniqueness + static thread_local std::unordered_map<std::string, int> mPrefixMap; + // lookup by name + static thread_local std::unordered_map<std::string, id> mNameMap; +}; #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index b17b9ff21e..b6d560a121 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -239,17 +239,12 @@ namespace LLError ~CallSite(); -#ifdef LL_LIBRARY_INCLUDE - bool shouldLog(); -#else // LL_LIBRARY_INCLUDE bool shouldLog() { return mCached ? mShouldLog : Log::shouldLog(*this); } - // this member function needs to be in-line for efficiency -#endif // LL_LIBRARY_INCLUDE void invalidate(); diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp index 83c53d220d..73a207504e 100644 --- a/indra/llcommon/llkeybind.cpp +++ b/indra/llcommon/llkeybind.cpp @@ -125,20 +125,16 @@ LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) bool LLKeyData::operator==(const LLKeyData& rhs) const { - if (mMouse != rhs.mMouse) return false; - if (mKey != rhs.mKey) return false; - if (mMask != rhs.mMask) return false; - if (mIgnoreMasks != rhs.mIgnoreMasks) return false; - return true; + return + (mMouse == rhs.mMouse) && + (mKey == rhs.mKey) && + (mMask == rhs.mMask) && + (mIgnoreMasks == rhs.mIgnoreMasks); } bool LLKeyData::operator!=(const LLKeyData& rhs) const { - if (mMouse != rhs.mMouse) return true; - if (mKey != rhs.mKey) return true; - if (mMask != rhs.mMask) return true; - if (mIgnoreMasks != rhs.mIgnoreMasks) return true; - return false; + return ! (*this == rhs); } bool LLKeyData::canHandle(const LLKeyData& data) const diff --git a/indra/llcommon/llpointer.cpp b/indra/llcommon/llpointer.cpp new file mode 100755 index 0000000000..adea447caa --- /dev/null +++ b/indra/llcommon/llpointer.cpp @@ -0,0 +1,26 @@ +/** + * @file llpointer.cpp + * @author Nat Goodspeed + * @date 2024-09-26 + * @brief Implementation for llpointer. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llpointer.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llerror.h" + +void LLPointerBase::wild_dtor(std::string_view msg) +{ +// LL_WARNS() << msg << LL_ENDL; + llassert_msg(false, msg); +} diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 048547e4cc..b53cfcdd1a 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -26,8 +26,9 @@ #ifndef LLPOINTER_H #define LLPOINTER_H -#include "llerror.h" // *TODO: consider eliminating this -#include "llmutex.h" +#include <boost/functional/hash.hpp> +#include <string_view> +#include <utility> // std::swap() //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s @@ -42,8 +43,18 @@ //---------------------------------------------------------------------------- +class LLPointerBase +{ +protected: + // alert the coder that a referenced type's destructor did something very + // strange -- this is in a non-template base class so we can hide the + // implementation in llpointer.cpp + static void wild_dtor(std::string_view msg); +}; + // Note: relies on Type having ref() and unref() methods -template <class Type> class LLPointer +template <class Type> +class LLPointer: public LLPointerBase { public: template<typename Subclass> @@ -60,6 +71,13 @@ public: ref(); } + // Even though the template constructors below accepting + // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to + // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&) + // constructors, the compiler recognizes these as The Copy Constructor and + // The Move Constructor, respectively. In other words, even in the + // presence of the LLPointer<Subclass> constructors, we still must specify + // the LLPointer<Type> constructors. LLPointer(const LLPointer<Type>& ptr) : mPointer(ptr.mPointer) { @@ -98,39 +116,52 @@ public: const Type& operator*() const { return *mPointer; } Type& operator*() { return *mPointer; } - operator BOOL() const { return (mPointer != nullptr); } operator bool() const { return (mPointer != nullptr); } bool operator!() const { return (mPointer == nullptr); } bool isNull() const { return (mPointer == nullptr); } bool notNull() const { return (mPointer != nullptr); } operator Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } + template <typename Type1> + bool operator !=(Type1* ptr) const { return (mPointer != ptr); } + template <typename Type1> + bool operator ==(Type1* ptr) const { return (mPointer == ptr); } + template <typename Type1> + bool operator !=(const LLPointer<Type1>& ptr) const { return (mPointer != ptr.mPointer); } + template <typename Type1> + bool operator ==(const LLPointer<Type1>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } LLPointer<Type>& operator =(Type* ptr) { - assign(ptr); + // copy-and-swap idiom, see http://gotw.ca/gotw/059.htm + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } + // Even though the template assignment operators below accepting + // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to + // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&) + // assignment operators, the compiler recognizes these as Copy Assignment + // and Move Assignment, respectively. In other words, even in the presence + // of the LLPointer<Subclass> assignment operators, we still must specify + // the LLPointer<Type> operators. LLPointer<Type>& operator =(const LLPointer<Type>& ptr) { - assign(ptr); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } LLPointer<Type>& operator =(LLPointer<Type>&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } @@ -138,210 +169,35 @@ public: template<typename Subclass> LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) { - assign(ptr.get()); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } template<typename Subclass> LLPointer<Type>& operator =(LLPointer<Subclass>&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } // Just exchange the pointers, which will not change the reference counts. static void swap(LLPointer<Type>& a, LLPointer<Type>& b) { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } - -protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *temp = mPointer; - mPointer = nullptr; - temp->unref(); - if (mPointer != nullptr) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } -#endif // LL_LIBRARY_INCLUDE - - void assign(const LLPointer<Type>& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - } - -protected: - Type* mPointer; -}; - -template <class Type> class LLConstPointer -{ - template<typename Subclass> - friend class LLConstPointer; -public: - LLConstPointer() : - mPointer(nullptr) - { - } - - LLConstPointer(const Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLConstPointer(const LLConstPointer<Type>& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - LLConstPointer(LLConstPointer<Type>&& ptr) noexcept - { - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer(const LLConstPointer<Subclass>& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - template<typename Subclass> - LLConstPointer(LLConstPointer<Subclass>&& ptr) noexcept : - mPointer(ptr.get()) - { - ptr.mPointer = nullptr; - } - - ~LLConstPointer() - { - unref(); - } - - const Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - const Type& operator*() const { return *mPointer; } - - operator BOOL() const { return (mPointer != nullptr); } - operator bool() const { return (mPointer != nullptr); } - bool operator!() const { return (mPointer == nullptr); } - bool isNull() const { return (mPointer == nullptr); } - bool notNull() const { return (mPointer != nullptr); } - - operator const Type*() const { return mPointer; } - bool operator !=(const Type* ptr) const { return (mPointer != ptr); } - bool operator ==(const Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLConstPointer<Type>& operator =(const Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - LLConstPointer<Type>& operator =(LLConstPointer<Type>&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - template<typename Subclass> - LLConstPointer<Type>& operator =(LLConstPointer<Subclass>&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; + using std::swap; // per Swappable convention + swap(a.mPointer, b.mPointer); } - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b) + // Put swap() overload in the global namespace, per Swappable convention + friend void swap(LLPointer<Type>& a, LLPointer<Type>& b) { - const Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; + LLPointer<Type>::swap(a, b); } protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else // LL_LIBRARY_INCLUDE void ref() { if (mPointer) @@ -354,22 +210,24 @@ protected: { if (mPointer) { - const Type *temp = mPointer; + Type *temp = mPointer; mPointer = nullptr; temp->unref(); if (mPointer != nullptr) { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + wild_dtor("Unreference did assignment to non-NULL because of destructor"); unref(); } } } -#endif // LL_LIBRARY_INCLUDE protected: - const Type* mPointer; + Type* mPointer; }; +template <typename Type> +using LLConstPointer = LLPointer<const Type>; + template<typename Type> class LLCopyOnWritePointer : public LLPointer<Type> { @@ -418,14 +276,14 @@ private: bool mStayUnique; }; -template<typename Type> -bool operator!=(Type* lhs, const LLPointer<Type>& rhs) +template<typename Type0, typename Type1> +bool operator!=(Type0* lhs, const LLPointer<Type1>& rhs) { return (lhs != rhs.get()); } -template<typename Type> -bool operator==(Type* lhs, const LLPointer<Type>& rhs) +template<typename Type0, typename Type1> +bool operator==(Type0* lhs, const LLPointer<Type1>& rhs) { return (lhs == rhs.get()); } diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 1c4ac5a7bf..0196a24b18 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -146,7 +146,7 @@ size_t LLQueuedThread::updateQueue(F32 max_time_ms) // schedule a call to threadedUpdate for every call to updateQueue if (!isQuitting()) { - mRequestQueue.post([=]() + mRequestQueue.post([=, this]() { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update"); mIdleThread = false; @@ -474,7 +474,7 @@ void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req) #else using namespace std::chrono_literals; auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms; - mRequestQueue.post([=] + mRequestQueue.post([=, this] { LL_PROFILE_ZONE_NAMED("processRequest - retry"); if (LL::WorkQueue::TimePoint::clock::now() < retry_time) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 33666964f7..21a663a003 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -170,7 +170,7 @@ fsyspath source_path(lua_State* L) { lua_getinfo(L, i, "s", &ar); } - return ar.source; + return { ar.source }; } } // namespace lluau @@ -1108,7 +1108,7 @@ lua_function(source_path, "source_path(): return the source path of the running { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).u8string()); + lua_pushstdstring(L, lluau::source_path(L)); return 1; } @@ -1119,7 +1119,7 @@ lua_function(source_dir, "source_dir(): return the source directory of the runni { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); + lua_pushstdstring(L, fsyspath(lluau::source_path(L).parent_path())); return 1; } @@ -1132,7 +1132,7 @@ lua_function(abspath, "abspath(path): " lua_checkdelta(L); auto path{ lua_tostdstring(L, 1) }; lua_pop(L, 1); - lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path).u8string()); + lua_pushstdstring(L, fsyspath(lluau::source_path(L).parent_path() / path)); return 1; } diff --git a/indra/llcommon/owning_ptr.h b/indra/llcommon/owning_ptr.h new file mode 100755 index 0000000000..7cf8d3f0ba --- /dev/null +++ b/indra/llcommon/owning_ptr.h @@ -0,0 +1,71 @@ +/** + * @file owning_ptr.h + * @author Nat Goodspeed + * @date 2024-09-27 + * @brief owning_ptr<T> is like std::unique_ptr<T>, but easier to integrate + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_OWNING_PTR_H) +#define LL_OWNING_PTR_H + +#include <functional> +#include <memory> + +/** + * owning_ptr<T> adapts std::unique_ptr<T> to make it easier to adopt into + * older code using dumb pointers. + * + * Consider a class Outer with a member Thing* mThing. After the constructor, + * each time a method wants to assign to mThing, it must test for nullptr and + * destroy the previous Thing instance. During Outer's lifetime, mThing is + * passed to legacy domain-specific functions accepting plain Thing*. Finally + * the destructor must again test for nullptr and destroy the remaining Thing + * instance. + * + * Multiply that by several different Outer members of different types, + * possibly with different domain-specific destructor functions. + * + * Dropping std::unique_ptr<Thing> into Outer is cumbersome for a several + * reasons. First, if Thing requires a domain-specific destructor function, + * the unique_ptr declaration of mThing must explicitly state the type of that + * function (as a function pointer, for a typical legacy function). Second, + * every Thing* assignment to mThing must be changed to mThing.reset(). Third, + * every time we call a legacy domain-specific function, we must pass + * mThing.get(). + * + * owning_ptr<T> is designed to drop into a situation like this. The domain- + * specific destructor function, if any, is passed to its constructor; it need + * not be encoded into the pointer type. owning_ptr<T> supports plain pointer + * assignment, internally calling std::unique_ptr<T>::reset(). It also + * supports implicit conversion to plain T*, to pass the owned pointer to + * legacy domain-specific functions. + * + * Obviously owning_ptr<T> must not be used in situations where ownership of + * the referenced object is passed on to another pointer: use std::unique_ptr + * for that. Similarly, it is not for shared ownership. It simplifies lifetime + * management for classes that currently store (and explicitly destroy) plain + * T* pointers. + */ +template <typename T> +class owning_ptr +{ + using deleter = std::function<void(T*)>; +public: + owning_ptr(T* p=nullptr, const deleter& d=std::default_delete<T>()): + mPtr(p, d) + {} + void reset(T* p=nullptr) { mPtr.reset(p); } + owning_ptr& operator=(T* p) { mPtr.reset(p); return *this; } + operator T*() const { return mPtr.get(); } + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr.operator->(); } + +private: + std::unique_ptr<T, deleter> mPtr; +}; + +#endif /* ! defined(LL_OWNING_PTR_H) */ diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index 9138c862f9..8b7b97a1f9 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -127,9 +127,7 @@ void LL::WorkQueueBase::error(const std::string& msg) void LL::WorkQueueBase::checkCoroutine(const std::string& method) { - // By convention, the default coroutine on each thread has an empty name - // string. See also LLCoros::logname(). - if (LLCoros::getName().empty()) + if (LLCoros::on_main_coro()) { LLTHROW(Error("Do not call " + method + " from a thread's default coroutine")); } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 6b14b68c78..1fb61673bd 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -27,10 +27,11 @@ #ifndef LL_LLIMAGE_H #define LL_LLIMAGE_H -#include "lluuid.h" -#include "llstring.h" +#include "llmutex.h" #include "llpointer.h" +#include "llstring.h" #include "lltrace.h" +#include "lluuid.h" constexpr S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2 constexpr S32 MAX_IMAGE_MIP = 12; // 4096x4096 diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 6037517103..d8f17561b3 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -31,6 +31,8 @@ #include "openjpeg.h" #include "event.h" #include "cio.h" +#include "owning_ptr.h" +#include <string> #define MAX_ENCODED_DISCARD_LEVELS 5 @@ -231,33 +233,6 @@ public: parameters.cp_reduce = discardLevel; } - ~JPEG2KDecode() - { - if (decoder) - { - opj_destroy_codec(decoder); - } - decoder = nullptr; - - if (image) - { - opj_image_destroy(image); - } - image = nullptr; - - if (stream) - { - opj_stream_destroy(stream); - } - stream = nullptr; - - if (codestream_info) - { - opj_destroy_cstr_info(&codestream_info); - } - codestream_info = nullptr; - } - bool readHeader( U8* data, U32 dataSize, @@ -275,11 +250,6 @@ public: return false; } - if (stream) - { - opj_stream_destroy(stream); - } - stream = opj_stream_create(dataSize, true); if (!stream) { @@ -301,13 +271,14 @@ public: opj_decoder_set_strict_mode(decoder, OPJ_FALSE); /* Read the main header of the codestream and if necessary the JP2 boxes*/ - if (!opj_read_header((opj_stream_t*)stream, decoder, &image)) + opj_image_t* img; + if (!opj_read_header(stream, decoder, &img)) { return false; } + image = img; codestream_info = opj_get_cstr_info(decoder); - if (!codestream_info) { return false; @@ -344,11 +315,6 @@ public: opj_set_warning_handler(decoder, opj_warn, this); opj_set_error_handler(decoder, opj_error, this); - if (stream) - { - opj_stream_destroy(stream); - } - stream = opj_stream_create(dataSize, true); if (!stream) { @@ -366,11 +332,7 @@ public: size = dataSize; offset = 0; - if (image) - { - opj_image_destroy(image); - image = nullptr; - } + image = nullptr; // needs to happen before opj_read_header and opj_decode... opj_set_decoded_resolution_factor(decoder, discard_level); @@ -378,10 +340,12 @@ public: // enable decoding partially loaded images opj_decoder_set_strict_mode(decoder, OPJ_FALSE); - if (!opj_read_header(stream, decoder, &image)) + opj_image_t* img; + if (!opj_read_header(stream, decoder, &img)) { return false; } + image = img; // needs to happen before decode which may fail if (channels) @@ -393,15 +357,9 @@ public: // count was zero. The latter is just a sanity check before we // dereference the array. - if (!decoded || !image || !image->numcomps) - { - opj_end_decompress(decoder, stream); - return false; - } - + bool result = (decoded && image && image->numcomps); opj_end_decompress(decoder, stream); - - return true; + return result; } opj_image_t* getImage() { return image; } @@ -409,10 +367,18 @@ public: private: opj_dparameters_t parameters; opj_event_mgr_t event_mgr; - opj_image_t* image = nullptr; - opj_codec_t* decoder = nullptr; - opj_stream_t* stream = nullptr; - opj_codestream_info_v2_t* codestream_info = nullptr; + owning_ptr<opj_codestream_info_v2_t> codestream_info{ + nullptr, + // opj_destroy_cstr_info(opj_codestream_info_v2_t**) requires a + // pointer to pointer, which is too bad because otherwise we could + // directly pass that function as the owning_ptr's deleter. + [](opj_codestream_info_v2_t* doomed) + { + opj_destroy_cstr_info(&doomed); + }}; + owning_ptr<opj_stream_t> stream{ nullptr, opj_stream_destroy }; + owning_ptr<opj_image_t> image{ nullptr, opj_image_destroy }; + owning_ptr<opj_codec_t> decoder{ nullptr, opj_destroy_codec }; }; class JPEG2KEncode : public JPEG2KBase @@ -447,43 +413,19 @@ public: parameters.irreversible = 1; } - if (comment_text) - { - free(comment_text); - } - comment_text = comment_text_in ? strdup(comment_text_in) : nullptr; + comment_text.assign(comment_text_in? comment_text_in : "no comment"); - parameters.cp_comment = comment_text ? comment_text : (char*)"no comment"; + // Because comment_text is a member declared before parameters, + // it will outlive parameters, so we can safely store in parameters a + // pointer into comment_text's data. Unfortunately cp_comment is + // declared as (non-const) char*. We just have to trust that this is + // legacy C style coding, rather than any intention to modify the + // comment string. (If there was actual modification, we could use a + // std::vector<char> instead, but let's only go there if we must.) + parameters.cp_comment = const_cast<char*>(comment_text.c_str()); llassert(parameters.cp_comment); } - ~JPEG2KEncode() - { - if (encoder) - { - opj_destroy_codec(encoder); - } - encoder = nullptr; - - if (image) - { - opj_image_destroy(image); - } - image = nullptr; - - if (stream) - { - opj_stream_destroy(stream); - } - stream = nullptr; - - if (comment_text) - { - free(comment_text); - } - comment_text = nullptr; - } - bool encode(const LLImageRaw& rawImageIn, LLImageJ2C &compressedImageOut) { LLImageDataSharedLock lockIn(&rawImageIn); @@ -561,11 +503,6 @@ public: memset(buffer, 0, data_size_guess); - if (stream) - { - opj_stream_destroy(stream); - } - stream = opj_stream_create(data_size_guess, false); if (!stream) { @@ -742,12 +679,12 @@ public: opj_image_t* getImage() { return image; } private: - opj_cparameters_t parameters; - opj_event_mgr_t event_mgr; - opj_image_t* image = nullptr; - opj_codec_t* encoder = nullptr; - opj_stream_t* stream = nullptr; - char* comment_text = nullptr; + std::string comment_text; + opj_cparameters_t parameters; + opj_event_mgr_t event_mgr; + owning_ptr<opj_stream_t> stream{ nullptr, opj_stream_destroy }; + owning_ptr<opj_image_t> image{ nullptr, opj_image_destroy }; + owning_ptr<opj_codec_t> encoder{ nullptr, opj_destroy_codec }; }; diff --git a/indra/llinventory/llsettingswater.cpp b/indra/llinventory/llsettingswater.cpp index 08e18ea26e..b30dbfeac2 100644 --- a/indra/llinventory/llsettingswater.cpp +++ b/indra/llinventory/llsettingswater.cpp @@ -239,15 +239,15 @@ void LLSettingsWater::blend(LLSettingsBase::ptr_t &end, F64 blendf) { mSettingFlags |= other->mSettingFlags; - mBlurMultiplier = lerp((F32)blendf, mBlurMultiplier, other->mBlurMultiplier); + mBlurMultiplier = lerp(mBlurMultiplier, other->mBlurMultiplier, (F32)blendf); lerpColor(mWaterFogColor, other->mWaterFogColor, (F32)blendf); - mWaterFogDensity = lerp((F32)blendf, mWaterFogDensity, other->mWaterFogDensity); - mFogMod = lerp((F32)blendf, mFogMod, other->mFogMod); - mFresnelOffset = lerp((F32)blendf, mFresnelOffset, other->mFresnelOffset); - mFresnelScale = lerp((F32)blendf, mFresnelScale, other->mFresnelScale); + mWaterFogDensity = lerp(mWaterFogDensity, other->mWaterFogDensity, (F32)blendf); + mFogMod = lerp(mFogMod, other->mFogMod, (F32)blendf); + mFresnelOffset = lerp(mFresnelOffset, other->mFresnelOffset, (F32)blendf); + mFresnelScale = lerp(mFresnelScale, other->mFresnelScale, (F32)blendf); lerpVector3(mNormalScale, other->mNormalScale, (F32)blendf); - mScaleAbove = lerp((F32)blendf, mScaleAbove, other->mScaleAbove); - mScaleBelow = lerp((F32)blendf, mScaleBelow, other->mScaleBelow); + mScaleAbove = lerp(mScaleAbove, other->mScaleAbove, (F32)blendf); + mScaleBelow = lerp(mScaleBelow, other->mScaleBelow, (F32)blendf); lerpVector2(mWave1Dir, other->mWave1Dir, (F32)blendf); lerpVector2(mWave2Dir, other->mWave2Dir, (F32)blendf); diff --git a/indra/llmath/llinterp.h b/indra/llmath/llinterp.h index ef6a5e638d..336539fb2e 100644 --- a/indra/llmath/llinterp.h +++ b/indra/llmath/llinterp.h @@ -29,11 +29,11 @@ #if defined(LL_WINDOWS) // macro definitions for common math constants (e.g. M_PI) are declared under the _USE_MATH_DEFINES // on Windows system. -// So, let's define _USE_MATH_DEFINES before including math.h +// So, let's define _USE_MATH_DEFINES before including cmath #define _USE_MATH_DEFINES #endif -#include "math.h" +#include <cmath> // Class from which different types of interpolators can be derived diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index fa315291a3..0348dad8fe 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -358,10 +358,7 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) return new_foo; } -inline F32 lerp(F32 a, F32 b, F32 u) -{ - return a + ((b - a) * u); -} +using std::lerp; inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v) { @@ -486,7 +483,7 @@ inline U32 get_next_power_two(U32 val, U32 max_power_two) //get the gaussian value given the linear distance from axis x and guassian value o inline F32 llgaussian(F32 x, F32 o) { - return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2*o*o)); + return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2.f*o*o)); } //helper function for removing outliers diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 700e61467b..00a56edf68 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1294,9 +1294,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1327,9 +1327,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), @@ -1354,9 +1354,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1494,8 +1494,8 @@ bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, for (S32 i=0;i<np;i++) { F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep); - mPath[i].mPos.set(lerp(0,params.getShear().mV[0],t), - lerp(0,params.getShear().mV[1],t), + mPath[i].mPos.set(lerp(0.f,params.getShear().mV[0],t), + lerp(0.f,params.getShear().mV[1],t), t - 0.5f); LLQuaternion quat; quat.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1); @@ -1559,10 +1559,10 @@ bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, { F32 t = (F32)i * mStep; mPath[i].mPos.set(0, - lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t), + lerp(0.f, -sin(F_PI*params.getTwist()*t)*0.5f,t), lerp(-0.5f, cos(F_PI*params.getTwist()*t)*0.5f,t)); - mPath[i].mScale.set(lerp(1,params.getScale().mV[0],t), - lerp(1,params.getScale().mV[1],t), 0,1); + mPath[i].mScale.set(lerp(1.f,params.getScale().mV[0],t), + lerp(1.f,params.getScale().mV[1],t), 0.f, 1.f); mPath[i].mTexT = t; LLQuaternion quat; quat.setQuat(F_PI * params.getTwist() * t,1,0,0); diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index bb0c94d513..d8f649140f 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -25,6 +25,7 @@ #include "linden_common.h" +#include "llmutex.h" #include "llvolumemgr.h" #include "llvolume.h" diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp index c0b5f48f2d..465e56b6f3 100644 --- a/indra/llmath/raytrace.cpp +++ b/indra/llmath/raytrace.cpp @@ -26,7 +26,7 @@ #include "linden_common.h" -#include "math.h" +#include <cmath> #include "v3math.h" #include "llquaternion.h" #include "m3math.h" diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 13972ad399..5539ca7b86 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -403,6 +403,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro( CoprocQueuePtr pendingCoprocs, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) { + std::string prevtask; for (;;) { // It is VERY IMPORTANT that we instantiate a new ptr_t just before @@ -424,10 +425,25 @@ void LLCoprocedurePool::coprocedureInvokerCoro( // destroyed during pop_wait_for(). QueuedCoproc::ptr_t coproc; boost::fibers::channel_op_status status; + // Each time control reaches our custom coroutine scheduler, we check + // how long the previous coroutine ran before yielding, and report + // coroutines longer than a certain cutoff. But these coprocedure pool + // coroutines are generic; the only way we know what work they're + // doing is the task 'status' set by LLCoros::setStatus(). But what if + // the coroutine runs the task to completion and returns to waiting? + // It does no good to report that "waiting" ran long. So each time we + // enter "waiting" status, also report the *previous* task name. + std::string waiting = "waiting", newstatus; + if (prevtask.empty()) { - LLCoros::TempStatus st("waiting for work for 10s"); - status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10)); + newstatus = waiting; } + else + { + newstatus = stringize("done ", prevtask, "; ", waiting); + } + LLCoros::setStatus(newstatus); + status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10)); if (status == boost::fibers::channel_op_status::closed) { break; @@ -436,6 +452,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro( if(status == boost::fibers::channel_op_status::timeout) { LL_DEBUGS_ONCE("CoProcMgr") << "pool '" << mPoolName << "' waiting." << LL_ENDL; + prevtask.clear(); continue; } // we actually popped an item @@ -446,6 +463,9 @@ void LLCoprocedurePool::coprocedureInvokerCoro( try { + // set "status" of pool coroutine to the name of the coproc task + prevtask = coproc->mName; + LLCoros::setStatus(prevtask); coproc->mProc(httpAdapter, coproc->mId); } catch (const LLCoros::Stop &e) diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index d3b80d684f..7ab25908e2 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1404,3 +1404,4 @@ char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->g char const* const _PREHASH_LargeGenericMessage = LLMessageStringTable::getInstance()->getString("LargeGenericMessage"); char const* const _PREHASH_GameControlInput = LLMessageStringTable::getInstance()->getString("GameControlInput"); char const* const _PREHASH_AxisData = LLMessageStringTable::getInstance()->getString("AxisData"); +char const* const _PREHASH_MetaData = LLMessageStringTable::getInstance()->getString("MetaData"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 5449eaf2a5..88dee7f961 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1405,5 +1405,6 @@ extern char const* const _PREHASH_ExperienceID; extern char const* const _PREHASH_LargeGenericMessage; extern char const* const _PREHASH_GameControlInput; extern char const* const _PREHASH_AxisData; +extern char const* const _PREHASH_MetaData; #endif diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 7fa4230237..b634600de0 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -1073,7 +1073,9 @@ bool LLDAELoader::OpenFile(const std::string& filename) LLModel* mdl = *i; if(mdl->getStatus() != LLModel::NO_ERRORS) { - setLoadState(ERROR_MODEL + mdl->getStatus()) ; + // setLoadState() values >= ERROR_MODEL are reserved to + // report errors with the model itself. + setLoadState(ERROR_MODEL + eLoadState(mdl->getStatus())) ; return false; //abort } diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index 480012699a..48249aa5c4 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -155,7 +155,7 @@ bool LLGLTFLoader::parseMeshes() } else { - setLoadState(ERROR_MODEL + pModel->getStatus()); + setLoadState(ERROR_MODEL + eLoadState(pModel->getStatus())); delete(pModel); return false; } diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index abbf90bf59..17c6247670 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -51,6 +51,7 @@ extern LL_COMMON_API bool on_main_thread(); //---------------------------------------------------------------------------- const F32 MIN_TEXTURE_LIFETIME = 10.f; +const F32 CONVERSION_SCRATCH_BUFFER_GL_VERSION = 3.29f; //which power of 2 is i? //assumes i is a power of 2 > 0 @@ -160,6 +161,7 @@ S32 LLImageGL::sMaxCategories = 1 ; bool LLImageGL::sSkipAnalyzeAlpha; U32 LLImageGL::sScratchPBO = 0; U32 LLImageGL::sScratchPBOSize = 0; +U32* LLImageGL::sManualScratch = nullptr; //------------------------ @@ -262,6 +264,22 @@ void LLImageGL::initClass(LLWindow* window, S32 num_catagories, bool skip_analyz } } +void LLImageGL::allocateConversionBuffer() +{ + if (gGLManager.mGLVersion < CONVERSION_SCRATCH_BUFFER_GL_VERSION) + { + try + { + sManualScratch = new U32[MAX_IMAGE_AREA]; + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS() << "Failed to allocate sManualScratch" << LL_ENDL; + } + } +} + //static void LLImageGL::cleanupClass() { @@ -273,6 +291,8 @@ void LLImageGL::cleanupClass() sScratchPBO = 0; sScratchPBOSize = 0; } + + delete[] sManualScratch; } @@ -1287,11 +1307,10 @@ void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures) void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void* pixels, bool allow_compression) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - std::unique_ptr<U32[]> scratch; if (LLRender::sGLCoreProfile) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - if (gGLManager.mGLVersion >= 3.29f) + if (gGLManager.mGLVersion >= CONVERSION_SCRATCH_BUFFER_GL_VERSION) { if (pixformat == GL_ALPHA) { //GL_ALPHA is deprecated, convert to RGBA @@ -1323,27 +1342,15 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_ALPHA is deprecated, convert to RGBA if (pixels != nullptr) { - try - { - scratch.reset(new U32[width * height]); - } - catch (std::bad_alloc) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height - << " Pixformat: GL_ALPHA, pixtype: GL_UNSIGNED_BYTE" << LL_ENDL; - } - U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) { - U8* pix = (U8*)&scratch[i]; + U8* pix = (U8*)&sManualScratch[i]; pix[0] = pix[1] = pix[2] = 0; pix[3] = ((U8*)pixels)[i]; } - pixels = scratch.get(); + pixels = sManualScratch; } pixformat = GL_RGBA; @@ -1354,30 +1361,18 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA if (pixels != nullptr) { - try - { - scratch.reset(new U32[width * height]); - } - catch (std::bad_alloc) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height - << " Pixformat: GL_LUMINANCE_ALPHA, pixtype: GL_UNSIGNED_BYTE" << LL_ENDL; - } - U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) { U8 lum = ((U8*)pixels)[i * 2 + 0]; U8 alpha = ((U8*)pixels)[i * 2 + 1]; - U8* pix = (U8*)&scratch[i]; + U8* pix = (U8*)&sManualScratch[i]; pix[0] = pix[1] = pix[2] = lum; pix[3] = alpha; } - pixels = scratch.get(); + pixels = sManualScratch; } pixformat = GL_RGBA; @@ -1388,29 +1383,17 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB if (pixels != nullptr) { - try - { - scratch.reset(new U32[width * height]); - } - catch (std::bad_alloc) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height - << " Pixformat: GL_LUMINANCE, pixtype: GL_UNSIGNED_BYTE" << LL_ENDL; - } - U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) { U8 lum = ((U8*)pixels)[i]; - U8* pix = (U8*)&scratch[i]; + U8* pix = (U8*)&sManualScratch[i]; pix[0] = pix[1] = pix[2] = lum; pix[3] = 255; } - pixels = scratch.get(); + pixels = sManualScratch; } pixformat = GL_RGBA; intformat = GL_RGB8; @@ -1789,7 +1772,7 @@ void LLImageGL::syncToMainThread(LLGLuint new_tex_name) ref(); LL::WorkQueue::postMaybe( mMainQueue, - [=]() + [=, this]() { LL_PROFILE_ZONE_NAMED("cglt - delete callback"); syncTexName(new_tex_name); diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index a8b94bd5b0..6b4492c09e 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -298,6 +298,7 @@ public: public: static void initClass(LLWindow* window, S32 num_catagories, bool skip_analyze_alpha = false, bool thread_texture_loads = false, bool thread_media_updates = false); + static void allocateConversionBuffer(); static void cleanupClass() ; private: @@ -305,6 +306,7 @@ private: static bool sSkipAnalyzeAlpha; static U32 sScratchPBO; static U32 sScratchPBOSize; + static U32* sManualScratch; //the flag to allow to call readBackRaw(...). //can be removed if we do not use that function at all. diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index ccdadc6ae0..91c0cfeec9 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -301,11 +301,11 @@ void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) void LLLuaFloater::showLuaFloater(const LLSD &data) { fsyspath fs_path(data["xml_path"].asString()); - std::string path = fs_path.lexically_normal().u8string(); + fsyspath path = fs_path.lexically_normal(); if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); - path = (fsyspath(lib_path) / path).u8string(); + path = fsyspath(fsyspath(lib_path) / path); } LLLuaFloater *floater = new LLLuaFloater(data); diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index ef0762fc17..eca13cbb3c 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -94,6 +94,7 @@ #include "llinitparam.h" #include "llinstancetracker.h" #include "llmortician.h" +#include "llmutex.h" #include "llnotificationptr.h" #include "llpointer.h" #include "llrefcount.h" diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 8093536868..3ed328e37f 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -423,6 +423,19 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const return ret; } +std::vector<LLSD> LLScrollListCtrl::getAllSelectedValues() const +{ + std::vector<LLSD> ret; + for (LLScrollListItem* item : mItemList) + { + if (item->getSelected()) + { + ret.push_back(item->getValue()); + } + } + return ret; +} + S32 LLScrollListCtrl::getNumSelected() const { S32 numSelected = 0; @@ -1510,7 +1523,7 @@ bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected) { if (selected) { - selectItem(item, -1); + selectItem(item, -1, !mAllowMultipleSelection); } else { diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index bfae08ab5b..badaf31657 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -284,6 +284,7 @@ public: LLScrollListItem* getFirstSelected() const; virtual S32 getFirstSelectedIndex() const; std::vector<LLScrollListItem*> getAllSelected() const; + std::vector<LLSD> getAllSelectedValues() const; S32 getNumSelected() const; LLScrollListItem* getLastSelectedItem() const { return mLastSelected; } diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 088fbe2744..81959f1542 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1602,8 +1602,14 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string) } } -void LLTextEditor::pasteTextWithLinebreaksImpl(const LLWString & clean_string) +void LLTextEditor::pasteTextWithLinebreaksImpl(const LLWString & clean_string, bool reset_cursor) { + if (reset_cursor) + { + deselect(); + setCursorPos(getLength()); + } + std::basic_string<llwchar>::size_type start = 0; std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 32dd95b8ac..0df2f6b38a 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -308,14 +308,13 @@ private: public: template <typename STRINGTYPE> - void pasteTextWithLinebreaks(const STRINGTYPE& clean_string) + void pasteTextWithLinebreaks(const STRINGTYPE& clean_string, bool reset_cursor = false) { - pasteTextWithLinebreaksImpl(ll_convert(clean_string)); + pasteTextWithLinebreaksImpl(ll_convert(clean_string), reset_cursor); } - void pasteTextWithLinebreaksImpl(const LLWString& clean_string); + void pasteTextWithLinebreaksImpl(const LLWString& clean_string, bool reset_cursor = false); private: - void pasteTextWithLinebreaksInternal(const LLWString & clean_string); void onKeyStroke(); // Concrete TextCmd sub-classes used by the LLTextEditor base class diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 0daa767766..a73962f3d6 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -45,6 +45,10 @@ static int16_t PLAYOUT_DEVICE_BAD = -2; static int16_t RECORD_DEVICE_DEFAULT = -1; static int16_t RECORD_DEVICE_BAD = -2; +static const std::string DEFAULT_DEVICE_NAME = "Default"; +static const std::string NO_DEVICE_NAME = "No Device"; +static const std::string NO_DEVICE_GUID; + LLAudioDeviceObserver::LLAudioDeviceObserver() : mSumVector {0}, mMicrophoneEnergy(0.0) {} float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } @@ -438,7 +442,7 @@ void ll_set_device_module_capture_device(rtc::scoped_refptr<webrtc::AudioDeviceM void LLWebRTCImpl::setCaptureDevice(const std::string &id) { int16_t recordingDevice = RECORD_DEVICE_DEFAULT; - if (id != "Default") + if (id != DEFAULT_DEVICE_NAME) { for (int16_t i = 0; i < mRecordingDeviceList.size(); i++) { @@ -502,7 +506,7 @@ void ll_set_device_module_render_device(rtc::scoped_refptr<webrtc::AudioDeviceMo void LLWebRTCImpl::setRenderDevice(const std::string &id) { int16_t playoutDevice = PLAYOUT_DEVICE_DEFAULT; - if (id != "Default") + if (id != DEFAULT_DEVICE_NAME) { for (int16_t i = 0; i < mPlayoutDeviceList.size(); i++) { @@ -546,6 +550,16 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) } } +bool LLWebRTCImpl::isCaptureNoDevice() +{ + return mRecordingDevice == mRecordingNoDevice; +} + +bool LLWebRTCImpl::isRenderNoDevice() +{ + return mPlayoutDevice == mPlayoutNoDevice; +} + // updateDevices needs to happen on the worker thread. void LLWebRTCImpl::updateDevices() { @@ -566,6 +580,11 @@ void LLWebRTCImpl::updateDevices() mTuningDeviceModule->PlayoutDeviceName(index, name, guid); mPlayoutDeviceList.emplace_back(name, guid); } + mPlayoutNoDevice = (int32_t)mPlayoutDeviceList.size(); + if (mPlayoutNoDevice) + { + mPlayoutDeviceList.emplace_back(NO_DEVICE_NAME, NO_DEVICE_GUID); + } int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); @@ -584,6 +603,11 @@ void LLWebRTCImpl::updateDevices() mTuningDeviceModule->RecordingDeviceName(index, name, guid); mRecordingDeviceList.emplace_back(name, guid); } + mRecordingNoDevice = (int32_t)mRecordingDeviceList.size(); + if (mRecordingNoDevice) + { + mRecordingDeviceList.emplace_back(NO_DEVICE_NAME, NO_DEVICE_GUID); + } for (auto &observer : mVoiceDevicesObserverList) { @@ -933,20 +957,20 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) void LLWebRTCPeerConnectionImpl::setMute(bool mute) { mMute = mute; + mute |= mWebRTCImpl->isCaptureNoDevice(); mWebRTCImpl->PostSignalingTask( - [this]() + [&]() { if (mPeerConnection) { auto senders = mPeerConnection->GetSenders(); - RTC_LOG(LS_INFO) << __FUNCTION__ << (mMute ? "disabling" : "enabling") << " streams count " << senders.size(); + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); for (auto &sender : senders) { - auto track = sender->track(); - if (track) + if (auto track = sender->track()) { - track->set_enabled(!mMute); + track->set_enabled(!mute); } } } @@ -960,6 +984,11 @@ void LLWebRTCPeerConnectionImpl::resetMute() void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) { + if (mWebRTCImpl->isRenderNoDevice()) + { + volume = 0; + } + mWebRTCImpl->PostSignalingTask( [this, volume]() { diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index c6fdb909dd..2deaba9e58 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -151,6 +151,9 @@ class LLWebRTCDeviceInterface virtual void setCaptureDevice(const std::string& id) = 0; virtual void setRenderDevice(const std::string& id) = 0; + virtual bool isCaptureNoDevice() = 0; + virtual bool isRenderNoDevice() = 0; + // Device observers for device change callbacks. virtual void setDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index b93a1fdb01..27b7eae8e7 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -210,6 +210,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS void setCaptureDevice(const std::string& id) override; void setRenderDevice(const std::string& id) override; + bool isCaptureNoDevice() override; + bool isRenderNoDevice() override; + void setTuningMode(bool enable) override; float getTuningAudioLevel() override; float getPeerConnectionAudioLevel() override; @@ -306,9 +309,11 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // accessors in native webrtc for devices aren't apparently implemented yet. bool mTuningMode; int32_t mRecordingDevice; + int32_t mRecordingNoDevice; LLWebRTCVoiceDeviceList mRecordingDeviceList; int32_t mPlayoutDevice; + int32_t mPlayoutNoDevice; LLWebRTCVoiceDeviceList mPlayoutDeviceList; bool mMute; diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 1883c6c9c1..b90e85d911 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1037,7 +1037,7 @@ F32 LLWindowMacOSX::getGamma() const &greenGamma, &blueMin, &blueMax, - &blueGamma) == noErr) + &blueGamma) == static_cast<CGError>(noErr)) { // So many choices... // Let's just return the green channel gamma for now. @@ -1088,7 +1088,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) &greenGamma, &blueMin, &blueMax, - &blueGamma) != noErr) + &blueGamma) != static_cast<CGError>(noErr)) { return false; } @@ -1103,7 +1103,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) gamma, blueMin, blueMax, - gamma) != noErr) + gamma) != static_cast<CGError>(noErr)) { return false; } @@ -1155,7 +1155,7 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) newPosition.y = screen_pos.mY; CGSetLocalEventsSuppressionInterval(0.0); - if(CGWarpMouseCursorPosition(newPosition) == noErr) + if(CGWarpMouseCursorPosition(newPosition) == static_cast<CGError>(noErr)) { result = true; } diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c086399375..7a29401bb6 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9289,7 +9289,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>1</real> + <real>1.5</real> </map> <key>RenderReflectionProbeDrawDistance</key> @@ -10100,7 +10100,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>1.0</real> + <real>0.7</real> </map> <key>RenderTonemapType</key> <map> @@ -10111,7 +10111,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>ReplaySession</key> <map> diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 24fd7928a6..553d6c1d32 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -77,7 +77,7 @@ RenderScreenSpaceReflections 1 1 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderDownScaleMethod 1 1 RenderCASSharpness 1 1 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 06ad730a40..b1359f8b91 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -77,7 +77,7 @@ RenderReflectionProbeLevel 1 3 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 1 diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ed66753267..f596ce04f8 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -500,7 +500,7 @@ void GLTFSceneManager::update() LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response) { LLAppViewer::instance()->postToMainCoro( - [=]() + [=, this]() { if (mUploadingAsset) { diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 6c539ade9b..2810cd6706 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -824,7 +824,7 @@ void LLAgentListener::getAgentScreenPos(LLSD const& event_data) { Response response(LLSD(), event_data); LLVector3 render_pos; - if (event_data.has("avatar_id") && (event_data["avatar_id"] != gAgentID)) + if (event_data.has("avatar_id") && (event_data["avatar_id"].asUUID() != gAgentID)) { LLUUID avatar_id(event_data["avatar_id"]); for (LLCharacter* character : LLCharacter::sInstances) diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp index c347d3e1be..d5f1e25151 100644 --- a/indra/newview/llcylinder.cpp +++ b/indra/newview/llcylinder.cpp @@ -29,7 +29,7 @@ #include "llcylinder.h" #include "llerror.h" -#include "math.h" +#include <cmath> #include "llmath.h" #include "noise.h" #include "v3math.h" diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 7a7824c7e6..362a02a642 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -60,7 +60,7 @@ bool LLFloaterLUADebug::postBuild() { LLCachedControl<bool> show_source_info(gSavedSettings, "LuaDebugShowSource", false); std::string source_info = show_source_info ? data["source_info"].asString() : ""; - mResultOutput->pasteTextWithLinebreaks(stringize(data["level"].asString(), source_info, data["msg"].asString())); + mResultOutput->pasteTextWithLinebreaks(stringize(data["level"].asString(), source_info, data["msg"].asString()), true); mResultOutput->addLineBreakChar(true); return false; }); diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index 0eba45ec29..6b3d87543a 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -51,9 +51,9 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) }, cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Script.Terminate", {[this](LLUICtrl*, const LLSD &userdata) { - if (mScriptList->hasSelectedItem()) + std::vector<LLSD> coros = mScriptList->getAllSelectedValues(); + for (auto coro_name : coros) { - std::string coro_name = mScriptList->getSelectedValue(); LLCoros::instance().killreq(coro_name); } }, cb_info::UNTRUSTED_BLOCK }); @@ -97,7 +97,7 @@ void LLFloaterLUAScripts::draw() void LLFloaterLUAScripts::populateScriptList() { S32 prev_pos = mScriptList->getScrollPos(); - LLSD prev_selected = mScriptList->getSelectedValue(); + std::vector<LLSD> prev_selected = mScriptList->getAllSelectedValues(); mScriptList->clearRows(); mScriptList->updateColumns(true); std::map<std::string, std::string> scripts = LLLUAmanager::getScriptNames(); @@ -112,7 +112,10 @@ void LLFloaterLUAScripts::populateScriptList() mScriptList->addElement(row); } mScriptList->setScrollPos(prev_pos); - mScriptList->setSelectedByValue(prev_selected, true); + for (auto value : prev_selected) + { + mScriptList->setSelectedByValue(value, true); + } } void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) @@ -120,10 +123,13 @@ void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) LLScrollListItem *item = mScriptList->hitItem(x, y); if (item) { - mScriptList->selectItemAt(x, y, MASK_NONE); + if (!item->getSelected()) + mScriptList->selectItemAt(x, y, MASK_NONE); + auto menu = mContextMenuHandle.get(); if (menu) { + menu->setItemEnabled(std::string("open_folder"), (mScriptList->getNumSelected() == 1)); menu->show(x, y); LLMenuGL::showPopup(this, menu, x, y); } diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 590cd09a31..f5b149335b 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -422,6 +422,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, U8 *binary_bucket, S32 binary_bucket_size, LLHost &sender, + LLSD metadata, LLUUID aux_id) { LLChat chat; @@ -451,6 +452,28 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, bool is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && LLMuteList::isLinden(name); + /*** + * The simulator may have flagged this sender as a bot, if the viewer would like to display + * the chat text in a different color or font, the below code is how the viewer can + * tell if the sender is a bot. + *----------------------------------------------------- + bool is_bot = false; + if (metadata.has("sender")) + { // The server has identified this sender as a bot. + is_bot = metadata["sender"]["bot"].asBoolean(); + } + *----------------------------------------------------- + */ + + std::string notice_name; + LLSD notice_args; + if (metadata.has("notice")) + { // The server has injected a notice into the IM conversation. + // These will be things like bot notifications, etc. + notice_name = metadata["notice"]["id"].asString(); + notice_args = metadata["notice"]["data"]; + } + chat.mMuted = is_muted; chat.mFromID = from_id; chat.mFromName = name; @@ -544,7 +567,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else { - // standard message, not from system + // standard message, server may have injected a notice into the conversation. std::string saved; if (offline == IM_OFFLINE) { @@ -579,8 +602,16 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, region_message = true; } } - gIMMgr->addMessage( - session_id, + + if (!notice_name.empty()) + { // The simulator has injected some sort of notice into the conversation. + // findString will only replace the contents of buffer if the notice_id is found. + LLTrans::findString(buffer, notice_name, notice_args); + name = SYSTEM_FROM; + from_id = LLUUID::null; + } + + gIMMgr->addMessage(session_id, from_id, name, buffer, @@ -592,6 +623,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, position, region_message, timestamp); + } else { @@ -1627,6 +1659,12 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) from_group = message_data["from_group"].asString() == "Y"; } + LLSD metadata; + if (message_data.has("metadata")) + { + metadata = message_data["metadata"]; + } + EInstantMessage dialog = static_cast<EInstantMessage>(message_data["dialog"].asInteger()); LLUUID session_id = message_data["transaction-id"].asUUID(); if (session_id.isNull() && dialog == IM_FROM_TASK) @@ -1654,6 +1692,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) local_bin_bucket.data(), S32(local_bin_bucket.size()), local_sender, + metadata, message_data["asset_id"].asUUID()); }); diff --git a/indra/newview/llimprocessing.h b/indra/newview/llimprocessing.h index 030d28b198..66ffc59ae0 100644 --- a/indra/newview/llimprocessing.h +++ b/indra/newview/llimprocessing.h @@ -48,6 +48,7 @@ public: U8 *binary_bucket, S32 binary_bucket_size, LLHost &sender, + LLSD metadata, LLUUID aux_id = LLUUID::null); // Either receives list of offline messages from 'ReadOfflineMsgs' capability diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 1402cc8c37..8a54267292 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3143,7 +3143,7 @@ void LLIMMgr::addMessage( const LLUUID& region_id, const LLVector3& position, bool is_region_msg, - U32 timestamp) // May be zero + U32 timestamp) // May be zero { LLUUID other_participant_id = target_id; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 4a65276384..9b6dcc5277 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -318,7 +318,7 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : void LLRequireResolver::findModule() { // If mPathToResolve is absolute, this replaces mSourceDir. - auto absolutePath = (mSourceDir / mPathToResolve).u8string(); + fsyspath absolutePath(mSourceDir / mPathToResolve); // Push _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); @@ -334,7 +334,7 @@ void LLRequireResolver::findModule() // not already cached - prep error message just in case auto fail{ - [L=L, path=mPathToResolve.u8string()]() + [L=L, path=mPathToResolve.string()]() { luaL_error(L, "could not find require('%s')", path.data()); }}; if (mPathToResolve.is_absolute()) @@ -349,10 +349,10 @@ void LLRequireResolver::findModule() { // if path is already absolute, operator/() preserves it auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); - std::string absolutePathOpt = (abspath / mPathToResolve).u8string(); + fsyspath absolutePathOpt = (abspath / mPathToResolve); if (absolutePathOpt.empty()) - luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data()); + luaL_error(L, "error requiring module '%s'", mPathToResolve.string().data()); if (findModuleImpl(absolutePathOpt)) return; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 5fc49b32ea..e4e70b02f9 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -548,8 +548,8 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate return ppTex ? (*ppTex).get() : NULL; } -volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -3959,7 +3959,7 @@ void LLMeshRepository::notifyLoadedMeshes() } // erase from background thread - mThread->mWorkQueue.post([=]() + mThread->mWorkQueue.post([=, this]() { mThread->mSkinMap.erase(id); }); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index d864a07615..c7572c9cbf 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -256,8 +256,8 @@ class LLMeshRepoThread : public LLThread { public: - volatile static S32 sActiveHeaderRequests; - volatile static S32 sActiveLODRequests; + static std::atomic<S32> sActiveHeaderRequests; + static std::atomic<S32> sActiveLODRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 0d6ca1cb94..2d659cd19d 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -440,7 +440,7 @@ void LLPanelEmojiComplete::updateConstraints() { mRenderRect = getLocalRect(); - mEmojiWidth = (U16)(mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2); + mEmojiWidth = (U16)(mIconFont->getWidthF32(LLWString(1, 0x1F431).c_str()) + mPadding * 2); if (mVertical) { mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 60017db51d..1c2dac6d6e 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -1307,7 +1307,7 @@ void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type showWearablesListView(); //e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE - applyListViewFilter(static_cast<EListViewItemType>(LVIT_SHAPE + type)); + applyListViewFilter(EListViewItemType(LVIT_SHAPE + EListViewItemType(type))); mWearableItemsList->setMenuWearableType(type); } diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 1af585708e..955b5e7730 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -777,7 +777,7 @@ void LLPanelPrimMediaControls::draw() else if(mFadeTimer.getStarted()) { F32 time = mFadeTimer.getElapsedTimeF32(); - alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + alpha *= llmax(lerp(1.0f, 0.0f, time / mControlFadeTime), 0.0f); if(time >= mControlFadeTime) { diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 60877494e7..e65ba523f0 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -42,7 +42,7 @@ static LLPanelInjector<LLPanelVoiceDeviceSettings> t_panel_group_general("panel_voice_device_settings"); static const std::string DEFAULT_DEVICE("Default"); - +static const std::string NO_DEVICE("No Device"); LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() : LLPanel() @@ -51,12 +51,10 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() mCtrlOutputDevices = NULL; mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - mDevicesUpdated = false; //obsolete mUseTuningMode = true; // grab "live" mic volume level mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - } LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() @@ -81,7 +79,7 @@ bool LLPanelVoiceDeviceSettings::postBuild() boost::bind(&LLPanelVoiceDeviceSettings::onCommitUnmute, this)); mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text"); - mLocalizedDeviceNames["No Device"] = getString("name_no_device"); + mLocalizedDeviceNames[NO_DEVICE] = getString("name_no_device"); mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device"); mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this)); @@ -115,7 +113,7 @@ void LLPanelVoiceDeviceSettings::draw() bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled(); if (voice_enabled) { - getChildView("wait_text")->setVisible( !is_in_tuning_mode && mUseTuningMode); + getChildView("wait_text")->setVisible(!is_in_tuning_mode && mUseTuningMode); getChildView("disabled_text")->setVisible(false); mUnmuteBtn->setVisible(false); } @@ -212,56 +210,29 @@ void LLPanelVoiceDeviceSettings::cancel() void LLPanelVoiceDeviceSettings::refresh() { - //grab current volume + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + + // grab current volume LLSlider* volume_slider = getChild<LLSlider>("mic_volume_slider"); + // set mic volume tuning slider based on last mic volume setting F32 current_volume = (F32)volume_slider->getValue().asReal(); - LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume); + voice_client->tuningSetMicVolume(current_volume); // Fill in popup menus - bool device_settings_available = LLVoiceClient::getInstance()->deviceSettingsAvailable(); + bool device_settings_available = voice_client->deviceSettingsAvailable(); + bool device_settings_updated = voice_client->deviceSettingsUpdated(); if (mCtrlInputDevices) { - mCtrlInputDevices->setEnabled(device_settings_available); - } - - if (mCtrlOutputDevices) - { - mCtrlOutputDevices->setEnabled(device_settings_available); - } - - getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available); - - if(!device_settings_available) - { - // The combo boxes are disabled, since we can't get the device settings from the daemon just now. - // Put the currently set default (ONLY) in the box, and select it. - if(mCtrlInputDevices) - { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM); - mCtrlInputDevices->setValue(mInputDevice); - } - if(mCtrlOutputDevices) - { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM); - mCtrlOutputDevices->setValue(mOutputDevice); - } - } - else if (LLVoiceClient::getInstance()->deviceSettingsUpdated()) - { - LLVoiceDeviceList::const_iterator device; - - if(mCtrlInputDevices) + if (device_settings_available && !voice_client->getCaptureDevices().empty()) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet + mCtrlInputDevices->setEnabled(true); + if (device_settings_updated) { mCtrlInputDevices->removeall(); mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - for (auto& device : devices) + for (auto& device : voice_client->getCaptureDevices()) { mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); } @@ -275,16 +246,24 @@ void LLPanelVoiceDeviceSettings::refresh() } } } + else + { + mCtrlInputDevices->setEnabled(false); + mCtrlInputDevices->removeall(); + mCtrlInputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE)); + } + } - if(mCtrlOutputDevices) + if (mCtrlOutputDevices) + { + if (device_settings_available && !voice_client->getRenderDevices().empty()) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet + mCtrlOutputDevices->setEnabled(true); + if (device_settings_updated) { mCtrlOutputDevices->removeall(); mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - - for (auto& device : devices) + for (auto& device : voice_client->getRenderDevices()) { mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); } @@ -298,7 +277,15 @@ void LLPanelVoiceDeviceSettings::refresh() } } } + else + { + mCtrlOutputDevices->setEnabled(false); + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE)); + } } + + getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available && !voice_client->getCaptureDevices().empty()); } void LLPanelVoiceDeviceSettings::initialize() diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h index 815396cbd1..d0d14c212c 100644 --- a/indra/newview/llpanelvoicedevicesettings.h +++ b/indra/newview/llpanelvoicedevicesettings.h @@ -63,7 +63,6 @@ protected: class LLComboBox *mCtrlInputDevices; class LLComboBox *mCtrlOutputDevices; class LLButton *mUnmuteBtn; - bool mDevicesUpdated; bool mUseTuningMode; std::map<std::string, std::string> mLocalizedDeviceNames; }; diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp index 82caa14433..66fbe0029a 100644 --- a/indra/newview/llsky.cpp +++ b/indra/newview/llsky.cpp @@ -39,7 +39,7 @@ // linden library includes #include "llerror.h" #include "llmath.h" -#include "math.h" +#include <cmath> #include "v4color.h" #include "llviewerobjectlist.h" diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp index e51aeb6080..09c57eaf1d 100644 --- a/indra/newview/llsprite.cpp +++ b/indra/newview/llsprite.cpp @@ -37,7 +37,7 @@ #include "llsprite.h" -#include "math.h" +#include <cmath> #include "lldrawable.h" #include "llface.h" diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index ed91214bd0..7b6f591254 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2832,7 +2832,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { LL_PROFILE_ZONE_SCOPED; - mRequestQueue.tryPost([=]() + mRequestQueue.tryPost([=, this]() { LLTextureFetchWorker* worker = getWorker(id); if (worker) @@ -3523,8 +3523,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) //if (! gViewerAssetStatsThread1) // return true; - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); + static std::atomic<bool> reporting_started(false); + static std::atomic<S32> report_sequence(0); // In mStatsSD, we have a copy we own of the LLSD representation // of the asset stats. Add some additional fields and ship it off. diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 92db598410..9633e54f29 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2903,14 +2903,14 @@ void LLViewerMediaImpl::update() media_tex->ref(); main_queue->postTo( mTexUpdateQueue, // Worker thread queue - [=]() // work done on update worker thread + [=, this]() // work done on update worker thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); #endif doMediaTexUpdate(media_tex, data, data_width, data_height, x_pos, y_pos, width, height, true); }, - [=]() // callback to main thread + [=, this]() // callback to main thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 33d9b5eb67..d722851851 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2139,6 +2139,21 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) EInstantMessage dialog = (EInstantMessage)d; LLHost sender = msg->getSender(); + LLSD metadata; + if (msg->getNumberOfBlocksFast(_PREHASH_MetaData) > 0) + { + S32 metadata_size = msg->getSizeFast(_PREHASH_MetaData, 0, _PREHASH_Data); + std::string metadata_buffer; + metadata_buffer.resize(metadata_size, 0); + + msg->getBinaryDataFast(_PREHASH_MetaData, _PREHASH_Data, &metadata_buffer[0], metadata_size, 0, metadata_size ); + std::stringstream metadata_stream(metadata_buffer); + if (LLSDSerialize::fromBinary(metadata, metadata_stream, metadata_size) == LLSDParser::PARSE_FAILURE) + { + metadata.clear(); + } + } + LLIMProcessing::processNewMessage(from_id, from_group, to_id, @@ -2153,7 +2168,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) position, binary_bucket, binary_bucket_size, - sender); + sender, + metadata); } void send_do_not_disturb_message (LLMessageSystem* msg, const LLUUID& from_id, const LLUUID& session_id) diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index c747319940..cc3e916270 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1330,7 +1330,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi // Check the whitelist, if there's media (otherwise just show it) if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) { - if ( obj != mDragHoveredObject) + if (obj != mDragHoveredObject) { // Highlight the dragged object LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); @@ -1935,6 +1935,11 @@ LLViewerWindow::LLViewerWindow(const Params& p) } LLFontManager::initClass(); + + // fonts use an GL_UNSIGNED_BYTE image format, + // so they need convertion, init buffers if needed + LLImageGL::allocateConversionBuffer(); + // Init font system, load default fonts and generate basic glyphs // currently it takes aprox. 0.5 sec and we would load these fonts anyway // before login screen. diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 1a35a71706..243cba6ffd 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -391,12 +391,21 @@ void LLVoiceClient::setRenderDevice(const std::string& name) LLWebRTCVoiceClient::getInstance()->setRenderDevice(name); } +bool LLVoiceClient::isCaptureNoDevice() +{ + return LLWebRTCVoiceClient::getInstance()->isCaptureNoDevice(); +} + +bool LLVoiceClient::isRenderNoDevice() +{ + return LLWebRTCVoiceClient::getInstance()->isRenderNoDevice(); +} + const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() { return LLWebRTCVoiceClient::getInstance()->getCaptureDevices(); } - const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() { return LLWebRTCVoiceClient::getInstance()->getRenderDevices(); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index d53f512d82..2731b0cc7f 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -192,6 +192,9 @@ public: virtual LLVoiceDeviceList& getCaptureDevices()=0; virtual LLVoiceDeviceList& getRenderDevices()=0; + virtual bool isCaptureNoDevice() = 0; + virtual bool isRenderNoDevice() = 0; + virtual void getParticipantList(std::set<LLUUID> &participants)=0; virtual bool isParticipant(const LLUUID& speaker_id)=0; //@} @@ -392,6 +395,8 @@ public: void setCaptureDevice(const std::string& name); void setRenderDevice(const std::string& name); + bool isCaptureNoDevice(); + bool isRenderNoDevice(); void setHidden(bool hidden); const LLVoiceDeviceList& getCaptureDevices(); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index bd0419f4dd..b8ddc1f255 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2775,6 +2775,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) } } } + void LLVivoxVoiceClient::setDevicesListUpdated(bool state) { mDevicesListUpdated = state; diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 3167705528..cec8b71442 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -121,6 +121,9 @@ public: void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; + bool isCaptureNoDevice() override { return false; }; + bool isRenderNoDevice() override { return false; }; + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index dbeccb51d8..6d4f3fe356 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -203,6 +203,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mTuningMode(false), mTuningMicGain(0.0), mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume + mDeviceSettingsAvailable(false), mDevicesListUpdated(false), mSpatialCoordsDirty(false), @@ -555,15 +556,19 @@ void LLWebRTCVoiceClient::connectionTimer() // to send position updates. updatePosition(); } - } - if (!sShuttingDown) - { - sessionState::processSessionStates(); - if (mProcessChannels && voiceEnabled && !mHidden) - { - sendPositionUpdate(false); - updateOwnVolume(); - } + LL::WorkQueue::postMaybe(mMainQueue, + [=, this] { + if (sShuttingDown) + { + return; + } + sessionState::processSessionStates(); + if (mProcessChannels && voiceEnabled && !mHidden) + { + sendPositionUpdate(false); + updateOwnVolume(); + } + }); } } catch (const LLContinueError&) @@ -640,12 +645,14 @@ void LLWebRTCVoiceClient::leaveAudioSession() void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; + mDeviceSettingsAvailable = false; mCaptureDevices.clear(); } void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) { LL_INFOS("Voice") << "Voice Capture Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL; + mDeviceSettingsAvailable = false; mCaptureDevices.push_back(device); } @@ -658,6 +665,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) { mWebRTCDeviceInterface->setCaptureDevice(name); } + void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { mDevicesListUpdated = state; @@ -669,7 +677,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi { LL::WorkQueue::postMaybe(mMainQueue, - [=] + [=, this] { OnDevicesChangedImpl(render_devices, capture_devices); }); @@ -703,20 +711,22 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi } setCaptureDevice(inputDevice); + mDeviceSettingsAvailable = true; setDevicesListUpdated(true); } void LLWebRTCVoiceClient::clearRenderDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; + mDeviceSettingsAvailable = false; mRenderDevices.clear(); } void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) { LL_INFOS("Voice") << "Voice Render Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL; + mDeviceSettingsAvailable = false; mRenderDevices.push_back(device); - } LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() @@ -729,6 +739,16 @@ void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) mWebRTCDeviceInterface->setRenderDevice(name); } +bool LLWebRTCVoiceClient::isCaptureNoDevice() +{ + return mCaptureDevices.empty() || mWebRTCDeviceInterface->isCaptureNoDevice(); +} + +bool LLWebRTCVoiceClient::isRenderNoDevice() +{ + return mRenderDevices.empty() || mWebRTCDeviceInterface->isRenderNoDevice(); +} + void LLWebRTCVoiceClient::tuningStart() { if (!mIsInTuningMode) @@ -754,11 +774,15 @@ bool LLWebRTCVoiceClient::inTuningMode() void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { - mTuningMicGain = volume; + mTuningMicGain = volume; } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) { + if (isRenderNoDevice()) + { + volume = 0; + } if (volume != mTuningSpeakerVolume) { @@ -768,14 +792,17 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::getAudioLevel() { - if (mIsInTuningMode) + if (isCaptureNoDevice()) { - return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f; + return 0; } - else + + if (mIsInTuningMode) { - return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f; + return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f; } + + return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f; } float LLWebRTCVoiceClient::tuningGetEnergy(void) @@ -783,15 +810,6 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void) return getAudioLevel(); } -bool LLWebRTCVoiceClient::deviceSettingsAvailable() -{ - bool result = true; - - if(mRenderDevices.empty() || mCaptureDevices.empty()) - result = false; - - return result; -} bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; @@ -801,7 +819,7 @@ bool LLWebRTCVoiceClient::deviceSettingsUpdated() void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) { - if(clearCurrentList) + if (clearCurrentList) { clearCaptureDevices(); clearRenderDevices(); @@ -809,7 +827,6 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) mWebRTCDeviceInterface->refreshDevices(); } - void LLWebRTCVoiceClient::setHidden(bool hidden) { mHidden = hidden; @@ -1523,9 +1540,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) { if (volume != mSpeakerVolume) { - { - mSpeakerVolume = volume; - } + mSpeakerVolume = volume; sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); } } @@ -1544,7 +1559,6 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain) } } - void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE; @@ -2207,7 +2221,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) @@ -2230,7 +2244,7 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs // callback from llwebrtc void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { mIceCandidates.push_back(candidate); }); } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2348,7 +2362,7 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2375,7 +2389,7 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2397,7 +2411,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; if (!mShutDown) { @@ -2411,7 +2425,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() void LLVoiceWebRTCConnection::OnPeerConnectionClosed() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Peer connection has closed." << LL_ENDL; if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_CLOSE) { @@ -2883,7 +2897,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // llwebrtc callback void LLVoiceWebRTCConnection::OnDataReceived(const std::string& data, bool binary) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); } // @@ -3039,7 +3053,7 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index 930018b123..88ead98950 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -114,7 +114,7 @@ public: /// @name Devices //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - bool deviceSettingsAvailable() override; + bool deviceSettingsAvailable() override { return mDeviceSettingsAvailable; } bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the WebRTC daemon for the current list of input/output devices. @@ -126,6 +126,9 @@ public: void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; + bool isCaptureNoDevice() override; + bool isRenderNoDevice() override; + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} @@ -462,8 +465,9 @@ private: bool mTuningMode; F32 mTuningMicGain; int mTuningSpeakerVolume; + bool mDeviceSettingsAvailable; bool mDevicesListUpdated; // set to true when the device list has been updated - // and false when the panelvoicedevicesettings has queried for an update status. + // and false when the panelvoicedevicesettings has queried for an update status. std::string mSpatialSessionCredentials; std::string mMainSessionGroupHandle; // handle of the "main" session group. diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index bf171fe954..d9fcd5811d 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llwatchdog.h" +#include "llmutex.h" #include "llthread.h" constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp index d8ea2b884f..d198f7a33b 100644 --- a/indra/newview/llworldmipmap.cpp +++ b/indra/newview/llworldmipmap.cpp @@ -30,7 +30,7 @@ #include "llviewercontrol.h" // LLControlGroup #include "llviewertexturelist.h" -#include "math.h" // log() +#include <cmath> // log() // Turn this on to output tile stats in the standard output #define DEBUG_TILES_STAT 0 diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 84f026699e..0b4b98d674 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -11248,21 +11248,24 @@ public: } }; - +// Called from LLViewHighlightTransparent when "Highlight Transparent" is toggled void LLPipeline::rebuildDrawInfo() { - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + const U32 types_to_traverse[] = { - LLViewerRegion* region = *iter; - - LLOctreeDirty dirty; - - LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME); - dirty.traverse(part->mOctree); + LLViewerRegion::PARTITION_VOLUME, + LLViewerRegion::PARTITION_BRIDGE, + LLViewerRegion::PARTITION_AVATAR + }; - part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE); - dirty.traverse(part->mOctree); + LLOctreeDirty dirty; + for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList()) + { + for (U32 type : types_to_traverse) + { + LLSpatialPartition* part = region->getSpatialPartition(type); + dirty.traverse(part->mOctree); + } } } diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml index e4f99d14e9..c4275d43f7 100644 --- a/indra/newview/skins/default/xui/da/strings.xml +++ b/indra/newview/skins/default/xui/da/strings.xml @@ -3723,6 +3723,10 @@ Hvis du bliver ved med at modtage denne besked, kontakt venligst [SUPPORT_SITE]. <string name="conference-title-incoming"> Konference med [AGENT_NAME] </string> + <string name="bot_warning"> +Du chatter med en bot, [NAME]. Del ikke personlige oplysninger. +Læs mere på https://second.life/scripted-agents. + </string> <string name="no_session_message"> (IM session eksisterer ikke) </string> diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 8464bd9b0c..dd003e72d9 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -1613,6 +1613,10 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_ <string name="conference-title-incoming">Konferenz mit [AGENT_NAME]</string> <string name="inventory_item_offered-im">Inventarobjekt „[ITEM_NAME]“ angeboten</string> <string name="inventory_folder_offered-im">Inventarordner „[ITEM_NAME]“ angeboten</string> + <string name="bot_warning"> + Sie chatten mit einem Bot, [NAME]. Geben Sie keine persönlichen Informationen weiter. +Erfahren Sie mehr unter https://second.life/scripted-agents. + </string> <string name="share_alert">Objekte aus dem Inventar hier her ziehen</string> <string name="facebook_post_success">Sie haben auf Facebook gepostet.</string> <string name="flickr_post_success">Sie haben auf Flickr gepostet.</string> diff --git a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml index 6859201650..211cea75d8 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml @@ -22,6 +22,7 @@ follows="all" layout="topleft" sort_column="script_name" + multi_select="true" name="scripts_list" top_pad="10" width="535"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index cb3e0e4b6a..a64b3eee36 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -371,7 +371,7 @@ layout="topleft" left="30" min_val="0.5" - max_val="1.5" + max_val="4.0" name="RenderExposure" show_text="true" top_pad="14" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 3ec4b7205b..9be4a8f737 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3720,6 +3720,10 @@ Please reinstall viewer from https://secondlife.com/support/downloads/ and cont <string name="inventory_folder_offered-im"> Inventory folder '[ITEM_NAME]' offered </string> + <string name="bot_warning"> + You are chatting with a bot, [NAME]. Do not share any personal information. +Learn more at https://second.life/scripted-agents. + </string> <string name="share_alert"> Drag items from inventory here </string> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 9fcfc2daa5..97e86e994c 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -1584,6 +1584,10 @@ Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conferencia con [AGENT_NAME]</string> <string name="inventory_item_offered-im">Ítem del inventario '[ITEM_NAME]' ofrecido</string> <string name="inventory_folder_offered-im">Carpeta del inventario '[ITEM_NAME]' ofrecida</string> + <string name="bot_warning"> +Estás conversando con un bot, [NAME]. No compartas información personal. +Más información en https://second.life/scripted-agents. + </string> <string name="share_alert">Arrastra los ítems desde el invenbtario hasta aquí</string> <string name="facebook_post_success">Has publicado en Facebook.</string> <string name="flickr_post_success">Has publicado en Flickr.</string> diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 55f6209fe1..60916ef92b 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -1614,6 +1614,10 @@ Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conférence avec [AGENT_NAME]</string> <string name="inventory_item_offered-im">Objet de l’inventaire [ITEM_NAME] offert</string> <string name="inventory_folder_offered-im">Dossier de l’inventaire [ITEM_NAME] offert</string> + <string name="bot_warning"> +Vous discutez avec un bot, [NAME]. Ne partagez pas d’informations personnelles. +En savoir plus sur https://second.life/scripted-agents. + </string> <string name="share_alert">Faire glisser les objets de l'inventaire ici</string> <string name="facebook_post_success">Vous avez publié sur Facebook.</string> <string name="flickr_post_success">Vous avez publié sur Flickr.</string> diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index f77ab1062a..88708a2b4d 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -1586,6 +1586,10 @@ Se il messaggio persiste, contatta [SUPPORT_SITE].</string> <string name="conference-title-incoming">Chiamata in conferenza con [AGENT_NAME]</string> <string name="inventory_item_offered-im">Offerto oggetto di inventario "[ITEM_NAME]"</string> <string name="inventory_folder_offered-im">Offerta cartella di inventario "[ITEM_NAME]"</string> + <string name="bot_warning"> +Stai parlando con un bot, [NAME]. Non condividere informazioni personali. +Scopri di più su https://second.life/scripted-agents. + </string> <string name="facebook_post_success">Hai pubblicato su Facebook.</string> <string name="flickr_post_success">Hai pubblicato su Flickr.</string> <string name="twitter_post_success">Hai pubblicato su Twitter.</string> diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index fa6c329fe7..ff3b1a53a2 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -6150,6 +6150,10 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ <string name="inventory_folder_offered-im"> フォルダ「[ITEM_NAME]」がインベントリに送られてきました。 </string> + <string name="bot_warning"> +[NAME]とチャットしています。個人情報を共有しないでください。 +詳細は https://second.life/scripted-agents をご覧ください。 + </string> <string name="share_alert"> インベントリからここにアイテムをドラッグします。 </string> diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 8032443020..65b487e1b3 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -4412,6 +4412,10 @@ Jeżeli nadal otrzymujesz ten komunikat, skontaktuj się z [SUPPORT_SITE]. <string name="inventory_folder_offered-im"> Zaoferowano folder: '[ITEM_NAME]' </string> + <string name="bot_warning"> +Rozmawiasz z botem [NAME]. Nie udostępniaj żadnych danych osobowych. +Dowiedz się więcej na https://second.life/scripted-agents. + </string> <string name="share_alert"> Przeciągaj tutaj rzeczy z Szafy </string> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 4ce1e6d2ec..9e66777b5a 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1549,6 +1549,10 @@ If you continue to receive this message, contact the [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conversa com [AGENT_NAME]</string> <string name="inventory_item_offered-im">Item do inventário '[ITEM_NAME]' oferecido</string> <string name="inventory_folder_offered-im">Pasta do inventário '[ITEM_NAME]' oferecida</string> + <string name="bot_warning"> +Você está conversando com um bot, [NAME]. Não compartilhe informações pessoais. +Saiba mais em https://second.life/scripted-agents. + </string> <string name="facebook_post_success">Você publicou no Facebook.</string> <string name="flickr_post_success">Você publicou no Flickr.</string> <string name="twitter_post_success">Você publicou no Twitter.</string> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 0079309ba2..174999ea36 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -4576,6 +4576,10 @@ support@secondlife.com. <string name="inventory_folder_offered-im"> Предложена папка инвентаря «[ITEM_NAME]» </string> + <string name="bot_warning"> +Вы общаетесь с ботом [NAME]. Не передавайте личные данные. +Подробнее на https://second.life/scripted-agents. + </string> <string name="share_alert"> Перетаскивайте вещи из инвентаря сюда </string> diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index fa2fd3a802..6c1f6506a2 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -4579,6 +4579,10 @@ Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun. <string name="inventory_folder_offered-im"> "[ITEM_NAME]" envanter klasörü sunuldu </string> + <string name="bot_warning"> +Bir bot ile sohbet ediyorsunuz, [NAME]. Kişisel bilgilerinizi paylaşmayın. +Daha fazla bilgi için: https://second.life/scripted-agents. + </string> <string name="share_alert"> Envanterinizden buraya öğeler sürükleyin </string> diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index bdb16c9bf1..a3a9915dc4 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -4573,6 +4573,10 @@ http://secondlife.com/support 求助解決問題。 <string name="inventory_folder_offered-im"> 收納區資料夾'[ITEM_NAME]'已向人提供 </string> + <string name="bot_warning"> +您正在与人工智能机器人 [NAME] 聊天。请勿分享任何个人信息。 +了解更多:https://second.life/scripted-agents。 + </string> <string name="share_alert"> 將收納區物品拖曳到這裡 </string> diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index f0a1b32eed..9687b68451 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -55,14 +55,14 @@ namespace tut // indra/newview/tests/llluamanager_test.cpp => // indra/newview auto newview{ fsyspath(__FILE__).parent_path().parent_path() }; - auto settings{ newview / "app_settings" / "settings.xml" }; + fsyspath settings{ newview / "app_settings" / "settings.xml" }; // true suppresses implicit declare; implicit declare requires // that every variable in settings.xml has a Comment, which many don't. - gSavedSettings.loadFromFile(settings.u8string(), true); + gSavedSettings.loadFromFile(settings, true); // At test time, since we don't have the app bundle available, // extend LuaRequirePath to include the require directory in the // source tree. - auto require{ (newview / "scripts" / "lua" / "require").u8string() }; + std::string require{ fsyspath(newview / "scripts" / "lua" / "require") }; auto paths{ gSavedSettings.getLLSD("LuaRequirePath") }; bool found = false; for (const auto& path : llsd::inArray(paths)) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index efc90b8991..18ff6cf7b6 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1032,6 +1032,12 @@ class Darwin_x86_64_Manifest(ViewerManifest): ): dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + # SDL2 + for libfile in ( + 'libSDL2-2.0.dylib', + ): + dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + # our apps executable_path = {} embedded_apps = [ (os.path.join("llplugin", "slplugin"), "SLPlugin.app") ] diff --git a/indra/test/sync.h b/indra/test/sync.h index 82eef1e5f5..abeb4e17a8 100644 --- a/indra/test/sync.h +++ b/indra/test/sync.h @@ -69,7 +69,7 @@ public: // misleading, as it will be emitted after waiting threads have // already awakened. But emitting the log message within the lock // would seem to hold the lock longer than we really ought. - LL_DEBUGS() << llcoro::logname() << " bump(" << n << ") -> " << updated << LL_ENDL; + LL_DEBUGS() << LLCoros::getName() << " bump(" << n << ") -> " << updated << LL_ENDL; } /** @@ -82,7 +82,7 @@ public: */ void set(int n) { - LL_DEBUGS() << llcoro::logname() << " set(" << n << ")" << LL_ENDL; + LL_DEBUGS() << LLCoros::getName() << " set(" << n << ")" << LL_ENDL; mCond.set_all(n); } @@ -101,7 +101,7 @@ public: private: void yield_until(const char* func, int arg, int until) { - std::string name(llcoro::logname()); + std::string name(LLCoros::getName()); LL_DEBUGS() << name << " yield_until(" << until << ") suspending" << LL_ENDL; if (! mCond.wait_for_equal(mTimeout, until)) { diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index bdabab70e0..cb330eba9b 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -129,8 +129,7 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) // Launch a coroutine with our login_() method. Run the coroutine until // its first wait; at that point, return here. - std::string coroname = - LLCoros::instance().launch("LLLogin::Impl::login_", [=]() { loginCoro(uri, login_params); }); + std::string coroname = LLCoros::instance().launch("LLLogin::Impl::login_", [=, this]() { loginCoro(uri, login_params); }); LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL; } |