diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2021-11-24 10:47:54 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2021-11-24 10:47:54 -0500 |
commit | 0b066539fe68dc5750900c3452189645c40adb45 (patch) | |
tree | fd01b6eaf174f7744fd82a28b0b948d301fd4d2b | |
parent | 78d837789a3741c65c3334934d96a505a522ee43 (diff) |
DRTVWR-546, SL-16220, SL-16094: Undo previous glthread branch revert.
Reverting a merge is sticky: it tells git you never want to see that branch
again. Merging the DRTVWR-546 branch, which contained the revert, into the
glthread branch undid much of the development work on that branch. To restore
it we must revert the revert.
This reverts commit 029b41c0419e975bbb28454538b46dc69ce5d2ba.
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 3 | ||||
-rw-r--r-- | indra/llcommon/llsingleton.h | 14 | ||||
-rw-r--r-- | indra/llcommon/tests/threadsafeschedule_test.cpp | 4 | ||||
-rw-r--r-- | indra/llcommon/tests/workqueue_test.cpp | 72 | ||||
-rw-r--r-- | indra/llcommon/timing.cpp | 25 | ||||
-rw-r--r-- | indra/llcommon/workqueue.cpp | 30 | ||||
-rw-r--r-- | indra/llcommon/workqueue.h | 197 | ||||
-rw-r--r-- | indra/llrender/llimagegl.cpp | 89 | ||||
-rw-r--r-- | indra/llrender/llimagegl.h | 27 | ||||
-rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 321 | ||||
-rw-r--r-- | indra/llwindow/llwindowwin32.h | 36 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 3 | ||||
-rw-r--r-- | indra/newview/app_settings/settings.xml | 25 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 51 | ||||
-rw-r--r-- | indra/newview/llmainlooprepeater.cpp | 88 | ||||
-rw-r--r-- | indra/newview/llmainlooprepeater.h | 64 | ||||
-rw-r--r-- | indra/newview/llstartup.cpp | 17 | ||||
-rw-r--r-- | indra/newview/llviewertexture.cpp | 34 | ||||
-rw-r--r-- | indra/newview/llviewertexture.h | 4 |
19 files changed, 371 insertions, 733 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 9defa6b6c1..782f656406 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -119,8 +119,8 @@ set(llcommon_SOURCE_FILES lluriparser.cpp lluuid.cpp llworkerthread.cpp - timing.cpp u64.cpp + threadpool.cpp workqueue.cpp StackWalker.cpp ) @@ -256,6 +256,7 @@ set(llcommon_HEADER_FILES lockstatic.h stdtypes.h stringize.h + threadpool.h threadsafeschedule.h timer.h tuple.h diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index f85f961287..6042c0906c 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -847,14 +847,13 @@ template<class T> class LLSimpleton { public: - static T* sInstance; - - static void createInstance() - { + template <typename... ARGS> + static void createInstance(ARGS&&... args) + { llassert(sInstance == nullptr); - sInstance = new T(); + sInstance = new T(std::forward<ARGS>(args)...); } - + static inline T* getInstance() { return sInstance; } static inline T& instance() { return *getInstance(); } static inline bool instanceExists() { return sInstance != nullptr; } @@ -864,6 +863,9 @@ public: delete sInstance; sInstance = nullptr; } + +private: + static T* sInstance; }; template <class T> diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index af67b9f492..c421cc7b1c 100644 --- a/indra/llcommon/tests/threadsafeschedule_test.cpp +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -46,11 +46,11 @@ namespace tut // the real time required for each push() call. Explicitly increment // the timestamp for each one -- but since we're passing explicit // timestamps, make the queue reorder them. - queue.push(Queue::TimeTuple(Queue::Clock::now() + 20ms, "ghi")); + queue.push(Queue::TimeTuple(Queue::Clock::now() + 200ms, "ghi")); // Given the various push() overloads, you have to match the type // exactly: conversions are ambiguous. queue.push("abc"s); - queue.push(Queue::Clock::now() + 10ms, "def"); + queue.push(Queue::Clock::now() + 100ms, "def"); queue.close(); auto entry = queue.pop(); ensure_equals("failed to pop first", std::get<0>(entry), "abc"s); diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp index d5405400fd..bea3ad911b 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -20,7 +20,10 @@ // external library headers // other Linden headers #include "../test/lltut.h" +#include "../test/catch_and_store_what_in.h" #include "llcond.h" +#include "llcoros.h" +#include "lleventcoro.h" #include "llstring.h" #include "stringize.h" @@ -138,7 +141,8 @@ namespace tut [](){ return 17; }, // Note that a postTo() *callback* can safely bind a reference to // a variable on the invoking thread, because the callback is run - // on the invoking thread. + // on the invoking thread. (Of course the bound variable must + // survive until the callback is called.) [&result](int i){ result = i; }); // this should post the callback to main qptr->runOne(); @@ -156,4 +160,70 @@ namespace tut main.runPending(); ensure_equals("failed to run string callback", alpha, "abc"); } + + template<> template<> + void object::test<5>() + { + set_test_name("postTo with void return"); + WorkQueue main("main"); + auto qptr = WorkQueue::getInstance("queue"); + std::string observe; + main.postTo( + qptr, + // The ONLY reason we can get away with binding a reference to + // 'observe' in our work callable is because we're directly + // calling qptr->runOne() on this same thread. It would be a + // mistake to do that if some other thread were servicing 'queue'. + [&observe](){ observe = "queue"; }, + [&observe](){ observe.append(";main"); }); + qptr->runOne(); + main.runOne(); + ensure_equals("failed to run both lambdas", observe, "queue;main"); + } + + template<> template<> + void object::test<6>() + { + set_test_name("waitForResult"); + std::string stored; + // Try to call waitForResult() on this thread's main coroutine. It + // should throw because the main coroutine must service the queue. + auto what{ catch_what<WorkQueue::Error>( + [this, &stored](){ stored = queue.waitForResult( + [](){ return "should throw"; }); }) }; + ensure("lambda should not have run", stored.empty()); + ensure_not("waitForResult() should have thrown", what.empty()); + ensure(STRINGIZE("should mention waitForResult: " << what), + what.find("waitForResult") != std::string::npos); + + // Call waitForResult() on a coroutine, with a string result. + LLCoros::instance().launch( + "waitForResult string", + [this, &stored]() + { stored = queue.waitForResult( + [](){ return "string result"; }); }); + llcoro::suspend(); + // Nothing will have happened yet because, even if the coroutine did + // run immediately, all it did was to queue the inner lambda on + // 'queue'. Service it. + queue.runOne(); + llcoro::suspend(); + ensure_equals("bad waitForResult return", stored, "string result"); + + // Call waitForResult() on a coroutine, with a void callable. + stored.clear(); + bool done = false; + LLCoros::instance().launch( + "waitForResult void", + [this, &stored, &done]() + { + queue.waitForResult([&stored](){ stored = "ran"; }); + done = true; + }); + llcoro::suspend(); + queue.runOne(); + llcoro::suspend(); + ensure_equals("didn't run coroutine", stored, "ran"); + ensure("void waitForResult() didn't return", done); + } } // namespace tut diff --git a/indra/llcommon/timing.cpp b/indra/llcommon/timing.cpp deleted file mode 100644 index c2dc695ef3..0000000000 --- a/indra/llcommon/timing.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file timing.cpp - * @brief This file will be deprecated in the future. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index e7d40354aa..c74dada2e4 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -26,8 +26,9 @@ using Mutex = LLCoros::Mutex; using Lock = LLCoros::LockType; -LL::WorkQueue::WorkQueue(const std::string& name): - super(makeName(name)) +LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity): + super(makeName(name)), + mQueue(capacity) { // TODO: register for "LLApp" events so we can implicitly close() on // viewer shutdown. @@ -38,6 +39,21 @@ void LL::WorkQueue::close() mQueue.close(); } +size_t LL::WorkQueue::size() +{ + return mQueue.size(); +} + +bool LL::WorkQueue::isClosed() +{ + return mQueue.isClosed(); +} + +bool LL::WorkQueue::done() +{ + return mQueue.done(); +} + void LL::WorkQueue::runUntilClose() { try @@ -130,3 +146,13 @@ void LL::WorkQueue::error(const std::string& msg) { LL_ERRS("WorkQueue") << msg << LL_ENDL; } + +void LL::WorkQueue::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()) + { + LLTHROW(Error("Do not call " + method + " from a thread's default coroutine")); + } +} diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index 8e4b38c2f3..96574a18b9 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -12,14 +12,14 @@ #if ! defined(LL_WORKQUEUE_H) #define LL_WORKQUEUE_H +#include "llcoros.h" +#include "llexception.h" #include "llinstancetracker.h" #include "threadsafeschedule.h" #include <chrono> +#include <exception> // std::current_exception #include <functional> // std::function -#include <queue> #include <string> -#include <utility> // std::pair -#include <vector> namespace LL { @@ -45,11 +45,16 @@ namespace LL using TimedWork = Queue::TimeTuple; using Closed = Queue::Closed; + struct Error: public LLException + { + Error(const std::string& what): LLException(what) {} + }; + /** * You may omit the WorkQueue name, in which case a unique name is * synthesized; for practical purposes that makes it anonymous. */ - WorkQueue(const std::string& name = std::string()); + WorkQueue(const std::string& name = std::string(), size_t capacity=1024); /** * Since the point of WorkQueue is to pass work to some other worker @@ -59,15 +64,36 @@ namespace LL */ void close(); + /** + * WorkQueue supports multiple producers and multiple consumers. In + * the general case it's misleading to test size(), since any other + * thread might change it the nanosecond the lock is released. On that + * basis, some might argue against publishing a size() method at all. + * + * But there are two specific cases in which a test based on size() + * might be reasonable: + * + * * If you're the only producer, noticing that size() == 0 is + * meaningful. + * * If you're the only consumer, noticing that size() > 0 is + * meaningful. + */ + size_t size(); + /// producer end: are we prevented from pushing any additional items? + bool isClosed(); + /// consumer end: are we done, is the queue entirely drained? + bool done(); + /*---------------------- fire and forget API -----------------------*/ /// fire-and-forget, but at a particular (future?) time template <typename CALLABLE> void post(const TimePoint& time, CALLABLE&& callable) { - // Defer reifying an arbitrary CALLABLE until we hit this method. - // All other methods should accept CALLABLEs of arbitrary type to - // avoid multiple levels of std::function indirection. + // Defer reifying an arbitrary CALLABLE until we hit this or + // postIfOpen(). All other methods should accept CALLABLEs of + // arbitrary type to avoid multiple levels of std::function + // indirection. mQueue.push(TimedWork(time, std::move(callable))); } @@ -83,6 +109,47 @@ namespace LL } /** + * post work for a particular time, unless the queue is closed before + * we can post + */ + template <typename CALLABLE> + bool postIfOpen(const TimePoint& time, CALLABLE&& callable) + { + // Defer reifying an arbitrary CALLABLE until we hit this or + // post(). All other methods should accept CALLABLEs of arbitrary + // type to avoid multiple levels of std::function indirection. + return mQueue.pushIfOpen(TimedWork(time, std::move(callable))); + } + + /** + * post work, unless the queue is closed before we can post + */ + template <typename CALLABLE> + bool postIfOpen(CALLABLE&& callable) + { + return postIfOpen(TimePoint::clock::now(), std::move(callable)); + } + + /** + * Post work to be run at a specified time to another WorkQueue, which + * may or may not still exist and be open. Return true if we were able + * to post. + */ + template <typename CALLABLE> + static bool postMaybe(weak_t target, const TimePoint& time, CALLABLE&& callable); + + /** + * Post work to another WorkQueue, which may or may not still exist + * and be open. Return true if we were able to post. + */ + template <typename CALLABLE> + static bool postMaybe(weak_t target, CALLABLE&& callable) + { + return postMaybe(target, TimePoint::clock::now(), + std::forward<CALLABLE>(callable)); + } + + /** * Launch a callable returning bool that will trigger repeatedly at * specified interval, until the callable returns false. * @@ -115,63 +182,8 @@ namespace LL // Studio compile errors that seem utterly unrelated to this source // code. template <typename CALLABLE, typename FOLLOWUP> - bool postTo(WorkQueue::weak_t target, - const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback) - { - // We're being asked to post to the WorkQueue at target. - // target is a weak_ptr: have to lock it to check it. - auto tptr = target.lock(); - if (! tptr) - // can't post() if the target WorkQueue has been destroyed - return false; - - // Here we believe target WorkQueue still exists. Post to it a - // lambda that packages our callable, our callback and a weak_ptr - // to this originating WorkQueue. - tptr->post( - time, - [reply = super::getWeak(), - callable = std::move(callable), - callback = std::move(callback)] - () - { - // Call the callable in any case -- but to minimize - // copying the result, immediately bind it into a reply - // lambda. The reply lambda also binds the original - // callback, so that when we, the originating WorkQueue, - // finally receive and process the reply lambda, we'll - // call the bound callback with the bound result -- on the - // same thread that originally called postTo(). - auto rlambda = - [result = callable(), - callback = std::move(callback)] - () - { callback(std::move(result)); }; - // Check if this originating WorkQueue still exists. - // Remember, the outer lambda is now running on a thread - // servicing the target WorkQueue, and real time has - // elapsed since postTo()'s tptr->post() call. - // reply is a weak_ptr: have to lock it to check it. - auto rptr = reply.lock(); - if (rptr) - { - // Only post reply lambda if the originating WorkQueue - // still exists. If not -- who would we tell? Log it? - try - { - rptr->post(std::move(rlambda)); - } - catch (const Closed&) - { - // Originating WorkQueue might still exist, but - // might be Closed. Same thing: just discard the - // callback. - } - } - }); - // looks like we were able to post() - return true; - } + bool postTo(weak_t target, + const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback); /** * Post work to another WorkQueue, requesting a specific callback to @@ -181,10 +193,36 @@ namespace LL * inaccessible. */ template <typename CALLABLE, typename FOLLOWUP> - bool postTo(WorkQueue::weak_t target, - CALLABLE&& callable, FOLLOWUP&& callback) + bool postTo(weak_t target, CALLABLE&& callable, FOLLOWUP&& callback) + { + return postTo(target, TimePoint::clock::now(), + std::move(callable), std::move(callback)); + } + + /** + * Post work to another WorkQueue to be run at a specified time, + * blocking the calling coroutine until then, returning the result to + * caller on completion. + * + * In general, we assume that each thread's default coroutine is busy + * servicing its WorkQueue or whatever. To try to prevent mistakes, we + * forbid calling waitForResult() from a thread's default coroutine. + */ + template <typename CALLABLE> + auto waitForResult(const TimePoint& time, CALLABLE&& callable); + + /** + * Post work to another WorkQueue, blocking the calling coroutine + * until then, returning the result to caller on completion. + * + * In general, we assume that each thread's default coroutine is busy + * servicing its WorkQueue or whatever. To try to prevent mistakes, we + * forbid calling waitForResult() from a thread's default coroutine. + */ + template <typename CALLABLE> + auto waitForResult(CALLABLE&& callable) { - return postTo(target, TimePoint::clock::now(), std::move(callable), std::move(callback)); + return waitForResult(TimePoint::clock::now(), std::move(callable)); } /*--------------------------- worker API ---------------------------*/ @@ -233,6 +271,23 @@ namespace LL bool runUntil(const TimePoint& until); private: + template <typename CALLABLE, typename FOLLOWUP> + static auto makeReplyLambda(CALLABLE&& callable, FOLLOWUP&& callback); + /// general case: arbitrary C++ return type + template <typename CALLABLE, typename FOLLOWUP, typename RETURNTYPE> + struct MakeReplyLambda; + /// specialize for CALLABLE returning void + template <typename CALLABLE, typename FOLLOWUP> + struct MakeReplyLambda<CALLABLE, FOLLOWUP, void>; + + /// general case: arbitrary C++ return type + template <typename CALLABLE, typename RETURNTYPE> + struct WaitForResult; + /// specialize for CALLABLE returning void + template <typename CALLABLE> + struct WaitForResult<CALLABLE, void>; + + static void checkCoroutine(const std::string& method); static void error(const std::string& msg); static std::string makeName(const std::string& name); void callWork(const Queue::DataTuple& work); @@ -254,8 +309,8 @@ namespace LL { public: // bind the desired data - BackJack(WorkQueue::weak_t target, - const WorkQueue::TimePoint& start, + BackJack(weak_t target, + const TimePoint& start, const std::chrono::duration<Rep, Period>& interval, CALLABLE&& callable): mTarget(target), @@ -302,8 +357,8 @@ namespace LL } private: - WorkQueue::weak_t mTarget; - WorkQueue::TimePoint mStart; + weak_t mTarget; + TimePoint mStart; std::chrono::duration<Rep, Period> mInterval; CALLABLE mCallable; }; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index ed0e3fb345..894eb8c773 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -172,31 +172,19 @@ BOOL is_little_endian() return (*c == 0x78) ; } -LLImageGLThread* LLImageGLThread::sInstance = nullptr; - //static void LLImageGL::initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha /* = false */) { LL_PROFILE_ZONE_SCOPED; sSkipAnalyzeAlpha = skip_analyze_alpha; - LLImageGLThread::sInstance = new LLImageGLThread(window); - LLImageGLThread::sInstance->start(); -} - -//static -void LLImageGL::updateClass() -{ - LL_PROFILE_ZONE_SCOPED; - LLImageGLThread::sInstance->executeCallbacks(); + LLImageGLThread::createInstance(window); } //static void LLImageGL::cleanupClass() { LL_PROFILE_ZONE_SCOPED; - LLImageGLThread::sInstance->mFunctionQueue.close(); - delete LLImageGLThread::sInstance; - LLImageGLThread::sInstance = nullptr; + LLImageGLThread::deleteSingleton(); } //static @@ -504,6 +492,9 @@ void LLImageGL::init(BOOL usemipmaps) #endif mCategory = -1; + + // Sometimes we have to post work for the main thread. + mMainQueue = LL::WorkQueue::getInstance("mainloop"); } void LLImageGL::cleanup() @@ -1536,8 +1527,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } //if we're on the image loading thread, be sure to delete old_texname and update mTexName on the main thread - if (LLImageGLThread::sInstance != nullptr && - LLThread::currentID() == LLImageGLThread::sInstance->getID()) + if (! on_main_thread()) { { LL_PROFILE_ZONE_NAMED("cglt - sync"); @@ -1554,7 +1544,9 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } ref(); - LLImageGLThread::sInstance->postCallback([=]() + LL::WorkQueue::postMaybe( + mMainQueue, + [=]() { LL_PROFILE_ZONE_NAMED("cglt - delete callback"); if (old_texname != 0) @@ -2259,7 +2251,11 @@ void LLImageGL::resetCurTexSizebar() */ LLImageGLThread::LLImageGLThread(LLWindow* window) - : LLThread("LLImageGL"), mWindow(window) + // We want exactly one thread, but a very large capacity: we never want + // anyone, especially inner-loop render code, to have to block on post() + // because we're full. + : ThreadPool("LLImageGL", 1, 1024*1024) + , mWindow(window) { LL_PROFILE_ZONE_SCOPED; mFinished = false; @@ -2268,61 +2264,6 @@ LLImageGLThread::LLImageGLThread(LLWindow* window) ThreadPool::start(); } -// post a function to be executed on the LLImageGL background thread - -bool LLImageGLThread::post(const std::function<void()>& func) -{ - try - { - mFunctionQueue.post(func); - } - catch (LLThreadSafeQueueInterrupt e) - { - return false; - } - - return true; -} - -//post a callback to be executed on the main thread - -bool LLImageGLThread::postCallback(const std::function<void()>& callback) -{ - try - { - if (!mCallbackQueue.tryPost(callback)) - { - mPendingCallbackQ.push(callback); - } - } - catch (LLThreadSafeQueueInterrupt e) - { - //thread is closing, drop request - return false; - } - - return true; -} - -void LLImageGLThread::executeCallbacks() -{ - LL_PROFILE_ZONE_SCOPED; - //executed from main thread - mCallbackQueue.runPending(); - - while (!mPendingCallbackQ.empty()) - { - if (mCallbackQueue.tryPost(mPendingCallbackQ.front())) - { - mPendingCallbackQ.pop(); - } - else - { - break; - } - } -} - void LLImageGLThread::run() { LL_PROFILE_ZONE_SCOPED; @@ -2330,7 +2271,7 @@ void LLImageGLThread::run() // WorkQueue, likewise cleanup afterwards. mWindow->makeContextCurrent(mContext); gGL.init(); - mFunctionQueue.runUntilClose(); + ThreadPool::run(); gGL.shutdown(); mWindow->destroySharedContext(mContext); } diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index bb46dbc639..ae773bb362 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -37,6 +37,7 @@ #include "llunits.h" #include "llthreadsafequeue.h" #include "llrender.h" +#include "threadpool.h" #include "workqueue.h" class LLTextureAtlas ; @@ -198,6 +199,7 @@ private: void freePickMask(); LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL + LL::WorkQueue::weak_t mMainQueue; U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel U16 mPickMaskWidth; U16 mPickMaskHeight; @@ -271,7 +273,6 @@ public: public: static void initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha = false); - static void updateClass(); static void cleanupClass() ; private: @@ -307,34 +308,24 @@ public: }; -class LLImageGLThread : public LLThread +class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool { public: LLImageGLThread(LLWindow* window); // post a function to be executed on the LLImageGL background thread - bool post(const std::function<void()>& func); - - //post a callback to be executed on the main thread - bool postCallback(const std::function<void()>& callback); - - void executeCallbacks(); + template <typename CALLABLE> + bool post(CALLABLE&& func) + { + return getQueue().postIfOpen(std::forward<CALLABLE>(func)); + } void run() override; - // Work Queue for background thread - LL::WorkQueue mFunctionQueue; - - // Work Queue for main thread (run from updateClass) - LL::WorkQueue mCallbackQueue; - +private: LLWindow* mWindow; void* mContext = nullptr; LLAtomicBool mFinished; - - std::queue<std::function<void()>> mPendingCallbackQ; - - static LLImageGLThread* sInstance; }; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 149a92ffff..062dd02903 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -183,23 +183,19 @@ DWORD LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC; LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1,-1); // The following class LLWinImm delegates Windows IMM APIs. -// We need this because some language versions of Windows, -// e.g., US version of Windows XP, doesn't install IMM32.DLL -// as a default, and we can't link against imm32.lib statically. -// I believe DLL loading of this type is best suited to do -// in a static initialization of a class. What I'm not sure is -// whether it follows the Linden Conding Standard... -// See http://wiki.secondlife.com/wiki/Coding_standards#Static_Members +// It was originally introduced to support US Windows XP, on which we needed +// to dynamically load IMM32.DLL and use GetProcAddress to resolve its entry +// points. Now that that's moot, we retain this wrapper only for hooks for +// metrics. class LLWinImm { public: - static bool isAvailable() { return sTheInstance.mHImmDll != NULL; } + static bool isAvailable() { return true; } public: // Wrappers for IMM API. static BOOL isIME(HKL hkl); - static HWND getDefaultIMEWnd(HWND hwnd); static HIMC getContext(HWND hwnd); static BOOL releaseContext(HWND hwnd, HIMC himc); static BOOL getOpenStatus(HIMC himc); @@ -213,236 +209,96 @@ public: static BOOL setCompositionFont(HIMC himc, LPLOGFONTW logfont); static BOOL setCandidateWindow(HIMC himc, LPCANDIDATEFORM candidate_form); static BOOL notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value); - -private: - LLWinImm(); - ~LLWinImm(); - -private: - // Pointers to IMM API. - BOOL (WINAPI *mImmIsIME)(HKL); - HWND (WINAPI *mImmGetDefaultIMEWnd)(HWND); - HIMC (WINAPI *mImmGetContext)(HWND); - BOOL (WINAPI *mImmReleaseContext)(HWND, HIMC); - BOOL (WINAPI *mImmGetOpenStatus)(HIMC); - BOOL (WINAPI *mImmSetOpenStatus)(HIMC, BOOL); - BOOL (WINAPI *mImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD); - BOOL (WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD); - BOOL (WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); - BOOL (WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); - LONG (WINAPI *mImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD); - BOOL (WINAPI *mImmSetCompositionString)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD); - BOOL (WINAPI *mImmSetCompositionFont)(HIMC, LPLOGFONTW); - BOOL (WINAPI *mImmSetCandidateWindow)(HIMC, LPCANDIDATEFORM); - BOOL (WINAPI *mImmNotifyIME)(HIMC, DWORD, DWORD, DWORD); - -private: - HMODULE mHImmDll; - static LLWinImm sTheInstance; }; -LLWinImm LLWinImm::sTheInstance; - -LLWinImm::LLWinImm() : mHImmDll(NULL) -{ - // Check system metrics - if ( !GetSystemMetrics( SM_IMMENABLED ) ) - return; - - mHImmDll = LoadLibraryA("Imm32"); - if (mHImmDll != NULL) - { - mImmIsIME = (BOOL (WINAPI *)(HKL)) GetProcAddress(mHImmDll, "ImmIsIME"); - mImmGetDefaultIMEWnd = (HWND (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetDefaultIMEWnd"); - mImmGetContext = (HIMC (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetContext"); - mImmReleaseContext = (BOOL (WINAPI *)(HWND, HIMC)) GetProcAddress(mHImmDll, "ImmReleaseContext"); - mImmGetOpenStatus = (BOOL (WINAPI *)(HIMC)) GetProcAddress(mHImmDll, "ImmGetOpenStatus"); - mImmSetOpenStatus = (BOOL (WINAPI *)(HIMC, BOOL)) GetProcAddress(mHImmDll, "ImmSetOpenStatus"); - mImmGetConversionStatus = (BOOL (WINAPI *)(HIMC, LPDWORD, LPDWORD)) GetProcAddress(mHImmDll, "ImmGetConversionStatus"); - mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmSetConversionStatus"); - mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmGetCompositionWindow"); - mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmSetCompositionWindow"); - mImmGetCompositionString= (LONG (WINAPI *)(HIMC, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmGetCompositionStringW"); - mImmSetCompositionString= (BOOL (WINAPI *)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmSetCompositionStringW"); - mImmSetCompositionFont = (BOOL (WINAPI *)(HIMC, LPLOGFONTW)) GetProcAddress(mHImmDll, "ImmSetCompositionFontW"); - mImmSetCandidateWindow = (BOOL (WINAPI *)(HIMC, LPCANDIDATEFORM)) GetProcAddress(mHImmDll, "ImmSetCandidateWindow"); - mImmNotifyIME = (BOOL (WINAPI *)(HIMC, DWORD, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmNotifyIME"); - - if (mImmIsIME == NULL || - mImmGetDefaultIMEWnd == NULL || - mImmGetContext == NULL || - mImmReleaseContext == NULL || - mImmGetOpenStatus == NULL || - mImmSetOpenStatus == NULL || - mImmGetConversionStatus == NULL || - mImmSetConversionStatus == NULL || - mImmGetCompostitionWindow == NULL || - mImmSetCompostitionWindow == NULL || - mImmGetCompositionString == NULL || - mImmSetCompositionString == NULL || - mImmSetCompositionFont == NULL || - mImmSetCandidateWindow == NULL || - mImmNotifyIME == NULL) - { - // If any of the above API entires are not found, we can't use IMM API. - // So, turn off the IMM support. We should log some warning message in - // the case, since it is very unusual; these APIs are available from - // the beginning, and all versions of IMM32.DLL should have them all. - // Unfortunately, this code may be executed before initialization of - // the logging channel (LL_WARNS()), and we can't do it here... Yes, this - // is one of disadvantages to use static constraction to DLL loading. - FreeLibrary(mHImmDll); - mHImmDll = NULL; - - // If we unload the library, make sure all the function pointers are cleared - mImmIsIME = NULL; - mImmGetDefaultIMEWnd = NULL; - mImmGetContext = NULL; - mImmReleaseContext = NULL; - mImmGetOpenStatus = NULL; - mImmSetOpenStatus = NULL; - mImmGetConversionStatus = NULL; - mImmSetConversionStatus = NULL; - mImmGetCompostitionWindow = NULL; - mImmSetCompostitionWindow = NULL; - mImmGetCompositionString = NULL; - mImmSetCompositionString = NULL; - mImmSetCompositionFont = NULL; - mImmSetCandidateWindow = NULL; - mImmNotifyIME = NULL; - } - } -} - - // static BOOL LLWinImm::isIME(HKL hkl) { - if ( sTheInstance.mImmIsIME ) - return sTheInstance.mImmIsIME(hkl); - return FALSE; + return ImmIsIME(hkl); } // static HIMC LLWinImm::getContext(HWND hwnd) { - if ( sTheInstance.mImmGetContext ) - return sTheInstance.mImmGetContext(hwnd); - return 0; + return ImmGetContext(hwnd); } //static BOOL LLWinImm::releaseContext(HWND hwnd, HIMC himc) { - if ( sTheInstance.mImmIsIME ) - return sTheInstance.mImmReleaseContext(hwnd, himc); - return FALSE; + return ImmReleaseContext(hwnd, himc); } // static BOOL LLWinImm::getOpenStatus(HIMC himc) { - if ( sTheInstance.mImmGetOpenStatus ) - return sTheInstance.mImmGetOpenStatus(himc); - return FALSE; + return ImmGetOpenStatus(himc); } // static BOOL LLWinImm::setOpenStatus(HIMC himc, BOOL status) { - if ( sTheInstance.mImmSetOpenStatus ) - return sTheInstance.mImmSetOpenStatus(himc, status); - return FALSE; + return ImmSetOpenStatus(himc, status); } // static BOOL LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence) { - if ( sTheInstance.mImmGetConversionStatus ) - return sTheInstance.mImmGetConversionStatus(himc, conversion, sentence); - return FALSE; + return ImmGetConversionStatus(himc, conversion, sentence); } // static BOOL LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence) { - if ( sTheInstance.mImmSetConversionStatus ) - return sTheInstance.mImmSetConversionStatus(himc, conversion, sentence); - return FALSE; + return ImmSetConversionStatus(himc, conversion, sentence); } // static BOOL LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form) { - if ( sTheInstance.mImmGetCompostitionWindow ) - return sTheInstance.mImmGetCompostitionWindow(himc, form); - return FALSE; + return ImmGetCompositionWindow(himc, form); } // static BOOL LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form) { - if ( sTheInstance.mImmSetCompostitionWindow ) - return sTheInstance.mImmSetCompostitionWindow(himc, form); - return FALSE; + return ImmSetCompositionWindow(himc, form); } // static LONG LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length) { - if ( sTheInstance.mImmGetCompositionString ) - return sTheInstance.mImmGetCompositionString(himc, index, data, length); - return FALSE; + return ImmGetCompositionString(himc, index, data, length); } // static BOOL LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength) { - if ( sTheInstance.mImmSetCompositionString ) - return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength); - return FALSE; + return ImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength); } // static BOOL LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont) { - if ( sTheInstance.mImmSetCompositionFont ) - return sTheInstance.mImmSetCompositionFont(himc, pFont); - return FALSE; + return ImmSetCompositionFont(himc, pFont); } // static BOOL LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form) { - if ( sTheInstance.mImmSetCandidateWindow ) - return sTheInstance.mImmSetCandidateWindow(himc, form); - return FALSE; + return ImmSetCandidateWindow(himc, form); } // static BOOL LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value) { - if ( sTheInstance.mImmNotifyIME ) - return sTheInstance.mImmNotifyIME(himc, action, index, value); - return FALSE; + return ImmNotifyIME(himc, action, index, value); } - -// ---------------------------------------------------------------------------------------- -LLWinImm::~LLWinImm() -{ - if (mHImmDll != NULL) - { - FreeLibrary(mHImmDll); - mHImmDll = NULL; - } -} - - class LLMonitorInfo { public: @@ -552,8 +408,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, : LLWindow(callbacks, fullscreen, flags) { sMainThreadId = LLThread::currentID(); - mWindowThread = new LLWindowWin32Thread(this); - mWindowThread->start(); + mWindowThread = new LLWindowWin32Thread(); //MAINT-516 -- force a load of opengl32.dll just in case windows went sideways LoadLibrary(L"opengl32.dll"); @@ -624,7 +479,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, // Make an instance of our window then define the window class mhInstance = GetModuleHandle(NULL); - mWndProc = NULL; // Init Direct Input - needed for joystick / Spacemouse @@ -1048,17 +902,13 @@ void LLWindowWin32::close() // Something killed the window while we were busy destroying gl or handle somehow got broken LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; } - mWindowHandle = NULL; - mWindowThread->mFinished = true; }); - - while (!mWindowThread->isStopped()) - { - //nudge window thread - PostMessage(mWindowHandle, WM_USER + 0x0017, 0xB0B0, 0x1337); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + // Even though the above lambda might not yet have run, we've already + // bound mWindowHandle into it by value, which should suffice for the + // operations we're asking. That's the last time WE should touch it. + mWindowHandle = NULL; + mWindowThread->close(); } BOOL LLWindowWin32::isValid() @@ -1351,51 +1201,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO << " Fullscreen: " << mFullscreen << LL_ENDL; - auto oldHandle = mWindowHandle; - - //zero out mWindowHandle and mhDC before destroying window so window thread falls back to peekmessage - mWindowHandle = 0; - mhDC = 0; - - if (oldHandle && !destroy_window_handler(oldHandle)) - { - LL_WARNS("Window") << "Failed to properly close window before recreating it!" << LL_ENDL; - } - - mWindowHandle = NULL; - mhDC = 0; - - mWindowThread->post( - [this, window_rect, dw_ex_style, dw_style]() - { - mWindowHandle = CreateWindowEx(dw_ex_style, - mWindowClassName, - mWindowTitle, - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, - window_rect.left, // x pos - window_rect.top, // y pos - window_rect.right - window_rect.left, // width - window_rect.bottom - window_rect.top, // height - NULL, - NULL, - mhInstance, - NULL); - - if (mWindowHandle) - { - mhDC = GetDC(mWindowHandle); - } - } - ); - - // HACK wait for above handle to become populated - // TODO: use a future - int count = 1024; - while (!mhDC && count > 0) - { - Sleep(10); - --count; - } + recreateWindow(window_rect, dw_ex_style, dw_style); if (mWindowHandle) { @@ -1723,48 +1529,7 @@ const S32 max_format = (S32)num_formats - 1; mhDC = 0; // Zero The Device Context } - auto oldHandle = mWindowHandle; - mWindowHandle = NULL; - mhDC = 0; - - // Destroy The Window - if (oldHandle && !destroy_window_handler(oldHandle)) - { - LL_WARNS("Window") << "Failed to properly close window!" << LL_ENDL; - } - - mWindowThread->post( - [this, window_rect, dw_ex_style, dw_style]() - { - mWindowHandle = CreateWindowEx(dw_ex_style, - mWindowClassName, - mWindowTitle, - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, - window_rect.left, // x pos - window_rect.top, // y pos - window_rect.right - window_rect.left, // width - window_rect.bottom - window_rect.top, // height - NULL, - NULL, - mhInstance, - NULL); - - if (mWindowHandle) - { - mhDC = GetDC(mWindowHandle); - } - } - ); - - // HACK wait for above handle to become populated - // TODO: use a future - int count = 1024; - while (!mhDC && count > 0) - { - PostMessage(oldHandle, WM_USER + 8, 0x1717, 0x3b3b); - Sleep(10); - --count; - } + recreateWindow(window_rect, dw_ex_style, dw_style); if (mWindowHandle) { @@ -2347,7 +2112,7 @@ void LLWindowWin32::gatherInput() } - if (mWindowThread->mFunctionQueue.size() > 0) + if (mWindowThread->getQueue().size()) { LL_PROFILE_ZONE_NAMED("gi - PostMessage"); kickWindowThread(); @@ -2455,17 +2220,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ if (NULL != window_imp) { - // Has user provided their own window callback? - if (NULL != window_imp->mWndProc) - { - LL_PROFILE_ZONE_NAMED("mwp - WndProc"); - if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param)) - { - // user has handled window message - return 0; - } - } - // Juggle to make sure we can get negative positions for when // mouse is outside window. LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param)); @@ -4746,10 +4500,8 @@ std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList() #endif // LL_WINDOWS -inline LLWindowWin32Thread::LLWindowWin32Thread(LLWindowWin32* window) - : LLThread("Window Thread"), - mWindow(window), - mFunctionQueue(MAX_QUEUE_SIZE) +inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() + : ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE) { ThreadPool::start(); } @@ -4813,7 +4565,7 @@ void LLWindowWin32::LLWindowWin32Thread::run() { MSG msg; BOOL status; - if (mWindow->mhDC == 0) + if (mhDC == 0) { LL_PROFILE_ZONE_NAMED("w32t - PeekMessage"); logger.onChange("PeekMessage(", std::hex, mWindowHandle, ")"); @@ -4840,11 +4592,7 @@ void LLWindowWin32::LLWindowWin32Thread::run() LL_PROFILE_ZONE_NAMED("w32t - Function Queue"); logger.onChange("runPending()"); //process any pending functions - std::function<void()> curFunc; - while (mFunctionQueue.tryPopBack(curFunc)) - { - curFunc(); - } + getQueue().runPending(); } #if 0 @@ -4857,11 +4605,6 @@ void LLWindowWin32::LLWindowWin32Thread::run() } } -void LLWindowWin32Thread::post(const std::function<void()>& func) -{ - mFunctionQueue.pushFront(func); -} - void LLWindowWin32::post(const std::function<void()>& func) { mFunctionQueue.pushFront(func); diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 8d0193abc8..b02815e990 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -36,44 +36,12 @@ #include "llthread.h" #include "llthreadsafequeue.h" #include "llmutex.h" +#include "workqueue.h" // Hack for async host by name #define LL_WM_HOST_RESOLVED (WM_APP + 1) typedef void (*LLW32MsgCallback)(const MSG &msg); -class LLWindowWin32; - -// Thread that owns the Window Handle -class LLWindowWin32Thread : public LLThread -{ -public: - class Message - { - public: - LRESULT mMsg; - }; - - static const int MAX_QUEUE_SIZE = 2048; - - LLThreadSafeQueue<MSG> mMessageQueue; - LLThreadSafeQueue<std::function<void()>> mFunctionQueue; - - bool mFinished = false; - - LLWindowWin32Thread(LLWindowWin32* window); - - void run() override; - - void post(const std::function<void()>& func); - -private: - - // call PeekMessage and pull enqueue messages for later processing - void gatherInput(); - LLWindowWin32* mWindow = nullptr; - -}; - class LLWindowWin32 : public LLWindow { public: @@ -218,7 +186,6 @@ protected: HGLRC mhRC = 0; // OpenGL rendering context HDC mhDC = 0; // Windows Device context handle HINSTANCE mhInstance; // handle to application instance - WNDPROC mWndProc; // user-installable window proc RECT mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse() WPARAM mLastSizeWParam; F32 mOverrideAspectRatio; @@ -280,7 +247,6 @@ protected: void kickWindowThread(HWND windowHandle=0); friend class LLWindowManager; - friend class LLWindowWin32Thread; }; class LLSplashScreenWin32 : public LLSplashScreen diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9b636e5e5d..5f085bb9ad 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -393,7 +393,6 @@ set(viewer_SOURCE_FILES llloginhandler.cpp lllogininstance.cpp llmachineid.cpp - llmainlooprepeater.cpp llmanip.cpp llmaniprotate.cpp llmanipscale.cpp @@ -1032,7 +1031,6 @@ set(viewer_HEADER_FILES llloginhandler.h lllogininstance.h llmachineid.h - llmainlooprepeater.h llmanip.h llmaniprotate.h llmanipscale.h @@ -1604,6 +1602,7 @@ if (WINDOWS) ${WINDOWS_LIBRARIES} comdlg32 dxguid + imm32 kernel32 odbc32 odbccp32 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2d821b7451..058da4b66d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3871,6 +3871,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>MainWorkTime</key> + <map> + <key>Comment</key> + <string>Max time per frame devoted to mainloop work queue (in milliseconds)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.1</real> + </map> <key>QueueInventoryFetchTimeout</key> <map> <key>Comment</key> @@ -12667,6 +12678,20 @@ <key>Value</key> <string /> </map> + <key>ThreadPoolSizes</key> + <map> + <key>Comment</key> + <string>Map of size overrides for specific thread pools.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>LLSD</string> + <key>Value</key> + <map> + <key>General</key> + <integer>4</integer> + </map> + </map> <key>ThrottleBandwidthKBPS</key> <map> <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 5b3cbff9b8..e13c0a2472 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -233,11 +233,12 @@ #include "llavatariconctrl.h" #include "llgroupiconctrl.h" #include "llviewerassetstats.h" +#include "workqueue.h" +using namespace LL; // Include for security api initialization #include "llsecapi.h" #include "llmachineid.h" -#include "llmainlooprepeater.h" #include "llcleanup.h" #include "llcoproceduremanager.h" @@ -366,6 +367,10 @@ BOOL gLogoutInProgress = FALSE; BOOL gSimulateMemLeak = FALSE; +// We don't want anyone, especially threads working on the graphics pipeline, +// to have to block due to this WorkQueue being full. +WorkQueue gMainloopWork("mainloop", 1024*1024); + //////////////////////////////////////////////////////////// // Internal globals... that should be removed. static std::string gArgs; @@ -381,42 +386,6 @@ static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; -//-- LLDeferredTaskList ------------------------------------------------------ - -/** - * A list of deferred tasks. - * - * We sometimes need to defer execution of some code until the viewer gets idle, - * e.g. removing an inventory item from within notifyObservers() may not work out. - * - * Tasks added to this list will be executed in the next LLAppViewer::idle() iteration. - * All tasks are executed only once. - */ -class LLDeferredTaskList: public LLSingleton<LLDeferredTaskList> -{ - LLSINGLETON_EMPTY_CTOR(LLDeferredTaskList); - LOG_CLASS(LLDeferredTaskList); - - friend class LLAppViewer; - typedef boost::signals2::signal<void()> signal_t; - - void addTask(const signal_t::slot_type& cb) - { - mSignal.connect(cb); - } - - void run() - { - if (!mSignal.empty()) - { - mSignal(); - mSignal.disconnect_all_slots(); - } - } - - signal_t mSignal; -}; - //---------------------------------------------------------------------------- // List of entries from strings.xml to always replace @@ -973,9 +942,6 @@ bool LLAppViewer::init() } LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ; - // Initialize the repeater service. - LLMainLoopRepeater::instance().start(); - // Initialize event recorder LLViewerEventRecorder::createInstance(); @@ -2217,8 +2183,6 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLProxy); LLCore::LLHttp::cleanup(); - LLMainLoopRepeater::instance().stop(); - ll_close_fail_log(); LLError::LLCallStacks::cleanup(); @@ -4550,7 +4514,7 @@ bool LLAppViewer::initCache() void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb) { - LLDeferredTaskList::instance().addTask(cb); + gMainloopWork.post(cb); } void LLAppViewer::loadKeyBindings() @@ -4948,7 +4912,6 @@ void LLAppViewer::idle() LLNotificationsUI::LLToast::updateClass(); LLSmoothInterpolation::updateInterpolants(); LLMortician::updateClass(); - LLImageGL::updateClass(); LLFilePickerThread::clearDead(); //calls LLFilePickerThread::notify() LLDirPickerThread::clearDead(); F32 dt_raw = idle_timer.getElapsedTimeAndResetF32(); diff --git a/indra/newview/llmainlooprepeater.cpp b/indra/newview/llmainlooprepeater.cpp deleted file mode 100644 index 6736e9a950..0000000000 --- a/indra/newview/llmainlooprepeater.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @file llmachineid.cpp - * @brief retrieves unique machine ids - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llapr.h" -#include "llevents.h" -#include "llmainlooprepeater.h" - - - -// LLMainLoopRepeater -//----------------------------------------------------------------------------- - - -LLMainLoopRepeater::LLMainLoopRepeater(void): - mQueue(0) -{ - ; // No op. -} - - -void LLMainLoopRepeater::start(void) -{ - if(mQueue != 0) return; - - mQueue = new LLThreadSafeQueue<LLSD>(1024); - mMainLoopConnection = LLEventPumps::instance(). - obtain("mainloop").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMainLoop, this, _1)); - mRepeaterConnection = LLEventPumps::instance(). - obtain("mainlooprepeater").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMessage, this, _1)); -} - - -void LLMainLoopRepeater::stop(void) -{ - mMainLoopConnection.release(); - mRepeaterConnection.release(); - - delete mQueue; - mQueue = 0; -} - - -bool LLMainLoopRepeater::onMainLoop(LLSD const &) -{ - LLSD message; - while(mQueue->tryPopBack(message)) { - std::string pump = message["pump"].asString(); - if(pump.length() == 0 ) continue; // No pump. - LLEventPumps::instance().obtain(pump).post(message["payload"]); - } - return false; -} - - -bool LLMainLoopRepeater::onMessage(LLSD const & event) -{ - try { - mQueue->pushFront(event); - } catch(LLThreadSafeQueueError & e) { - LL_WARNS() << "could not repeat message (" << e.what() << ")" << - event.asString() << LL_ENDL; - } - return false; -} diff --git a/indra/newview/llmainlooprepeater.h b/indra/newview/llmainlooprepeater.h deleted file mode 100644 index 2ec3a74e4a..0000000000 --- a/indra/newview/llmainlooprepeater.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file llmainlooprepeater.h - * @brief a service for repeating messages on the main loop. - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLMAINLOOPREPEATER_H -#define LL_LLMAINLOOPREPEATER_H - - -#include "llsd.h" -#include "llthreadsafequeue.h" - - -// -// A service which creates the pump 'mainlooprepeater' to which any thread can -// post a message that will be re-posted on the main loop. -// -// The posted message should contain two map elements: pump and payload. The -// pump value is a string naming the pump to which the message should be -// re-posted. The payload value is what will be posted to the designated pump. -// -class LLMainLoopRepeater: - public LLSingleton<LLMainLoopRepeater> -{ - LLSINGLETON(LLMainLoopRepeater); -public: - // Start the repeater service. - void start(void); - - // Stop the repeater service. - void stop(void); - -private: - LLTempBoundListener mMainLoopConnection; - LLTempBoundListener mRepeaterConnection; - LLThreadSafeQueue<LLSD> * mQueue; - - bool onMainLoop(LLSD const &); - bool onMessage(LLSD const & event); -}; - - -#endif diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 8d21b04511..df066fb7ed 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -206,6 +206,9 @@ #include "llstacktrace.h" +#include "threadpool.h" + + #if LL_WINDOWS #include "lldxhardware.h" #endif @@ -303,6 +306,20 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is // local classes // +void launchThreadPool() +{ + LLSD poolSizes{ gSavedSettings.getLLSD("ThreadPoolSizes") }; + LLSD sizeSpec{ poolSizes["General"] }; + LLSD::Integer size{ sizeSpec.isInteger()? sizeSpec.asInteger() : 3 }; + LL_DEBUGS("ThreadPool") << "Instantiating General pool with " + << size << " threads" << LL_ENDL; + // Use a function-static ThreadPool: static duration, but instantiated + // only on demand. + // We don't want anyone, especially the main thread, to have to block + // due to this ThreadPool being full. + static LL::ThreadPool pool("General", size, 1024*1024); +} + void update_texture_fetch() { LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index e6ac701644..f932acd48c 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -679,6 +679,9 @@ void LLViewerTexture::init(bool firstinit) mVolumeList[LLRender::LIGHT_TEX].clear(); mVolumeList[LLRender::SCULPT_TEX].clear(); + + mMainQueue = LL::WorkQueue::getInstance("mainloop"); + mImageQueue = LL::WorkQueue::getInstance("LLImageGL"); } //virtual @@ -1622,17 +1625,26 @@ void LLViewerFetchedTexture::scheduleCreateTexture() { mNeedsCreateTexture = TRUE; #if LL_WINDOWS //flip to 0 to revert to single-threaded OpenGL texture uploads - if (!LLImageGLThread::sInstance->post([this]() - { - //actually create the texture on a background thread - createTexture(); - LLImageGLThread::sInstance->postCallback([this]() - { - //finalize on main thread - postCreateTexture(); - unref(); - }); - })) + auto mainq = mMainQueue.lock(); + if (mainq) + { + mainq->postTo( + mImageQueue, + // work to be done on LLImageGL worker thread + [this]() + { + //actually create the texture on a background thread + createTexture(); + }, + // callback to be run on main thread + [this]() + { + //finalize on main thread + postCreateTexture(); + unref(); + }); + } + else #endif { gTextureList.mCreateTextureList.insert(this); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index f9f1bfef44..4cd4c7cd39 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -35,6 +35,7 @@ #include "llrender.h" #include "llmetricperformancetester.h" #include "httpcommon.h" +#include "workqueue.h" #include <map> #include <list> @@ -213,6 +214,9 @@ protected: //do not use LLPointer here. LLViewerMediaTexture* mParcelMedia ; + LL::WorkQueue::weak_t mMainQueue; + LL::WorkQueue::weak_t mImageQueue; + static F32 sTexelPixelRatio; public: static const U32 sCurrentFileVersion; |