diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index e953451d49..3f3ef2be8a 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -442,7 +442,15 @@ S32 LLQueuedThread::processNextRequest() // safe to access req. if (req) { - // process request + // Image thread pool from CoolVL + if (req->getFlags() & FLAG_ASYNC) + { + req->processRequest(); + return getPending(); + } + // + + // process request bool complete = req->processRequest(); if (complete) diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index 5d3f873646..858d578b7f 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -67,6 +67,7 @@ public: FLAG_AUTO_COMPLETE = 1, FLAG_AUTO_DELETE = 2, // child-class dependent FLAG_ABORT = 4 + ,FLAG_ASYNC = 8 // Image thread pool from CoolVL }; typedef U32 handle_t; diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index 84b03a9e06..db4e3d2de7 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -29,13 +29,100 @@ #include "llimageworker.h" #include "llimagedxt.h" + // Image thread pool from CoolVL +#include "boost/thread.hpp" +std::atomic< U32 > s_ChildThreads; + +class PoolWorkerThread : public LLThread +{ +public: + PoolWorkerThread(std::string name) : LLThread(name), + mCurrentRequest(NULL) + { + } + virtual void run() + { + while (!isQuitting()) + { + auto *pReq = mCurrentRequest.exchange(nullptr); + + if (pReq) + pReq->processRequestIntern(); + checkPause(); + } + } + bool isBusy() + { + auto *pReq = mCurrentRequest.load(); + if (!pReq) + return false; + + auto status = pReq->getStatus(); + + return status == LLQueuedThread::STATUS_INPROGRESS || status == LLQueuedThread::STATUS_INPROGRESS; + } + + bool runCondition() + { + return mCurrentRequest != NULL; + } + + bool setRequest(LLImageDecodeThread::ImageRequest* req) + { + LLImageDecodeThread::ImageRequest* pOld{ nullptr }; + bool bSuccess = mCurrentRequest.compare_exchange_strong(pOld, req); + wake(); + + return bSuccess; + } + +private: + std::atomic< LLImageDecodeThread::ImageRequest * > mCurrentRequest; +}; +// + //---------------------------------------------------------------------------- // MAIN THREAD -LLImageDecodeThread::LLImageDecodeThread(bool threaded) +LLImageDecodeThread::LLImageDecodeThread(bool threaded, U32 aSubThreads) : LLQueuedThread("imagedecode", threaded) { mCreationMutex = new LLMutex(); + + // Image thread pool from CoolVL + if (aSubThreads == 0) + { + aSubThreads = boost::thread::hardware_concurrency(); + if (!aSubThreads) + aSubThreads = 4U; // Use a sane default: 4 cores + if (aSubThreads > 8U) + { + // Using number of (virtual) cores - 1 (for the main image worker + // thread) - 1 (for the viewer main loop thread), further bound to + // a maximum of 32 threads (more than that is totally useless, even + // when flying over main land with 512m draw distance). + aSubThreads = llmin(aSubThreads - 2U, 32U); + } + else if (aSubThreads > 2U) + { + // Using number of (virtual) cores - 1 (for the main image worker + // thread). + --aSubThreads; + } + } + else if (aSubThreads == 1) // Disable if only 1 + aSubThreads = 0; + + s_ChildThreads = aSubThreads; + for (U32 i = 0; i < aSubThreads; ++i) + { + std::stringstream strm; + strm << "imagedecodethread" << (i + 1); + + mThreadPool.push_back(std::make_shared< PoolWorkerThread>(strm.str())); + mThreadPool[i]->start(); + } + // } //virtual @@ -53,9 +140,12 @@ S32 LLImageDecodeThread::update(F32 max_time_ms) iter != mCreationList.end(); ++iter) { creation_info& info = *iter; + // ImageRequest* req = new ImageRequest(info.handle, info.image, + // info.priority, info.discard, info.needs_aux, + // info.responder); ImageRequest* req = new ImageRequest(info.handle, info.image, - info.priority, info.discard, info.needs_aux, - info.responder); + info.priority, info.discard, info.needs_aux, + info.responder, this); bool res = addRequest(req); if (!res) @@ -95,15 +185,21 @@ LLImageDecodeThread::Responder::~Responder() LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image, U32 priority, S32 discard, BOOL needs_aux, - LLImageDecodeThread::Responder* responder) + LLImageDecodeThread::Responder* responder, + LLImageDecodeThread *aQueue) : LLQueuedThread::QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE), mFormattedImage(image), mDiscardLevel(discard), mNeedsAux(needs_aux), mDecodedRaw(FALSE), mDecodedAux(FALSE), - mResponder(responder) + mResponder(responder), + mQueue( aQueue ) // Image thread pool from CoolVL { + // Image thread pool from CoolVL + if (s_ChildThreads > 0) + mFlags |= FLAG_ASYNC; + // } LLImageDecodeThread::ImageRequest::~ImageRequest() @@ -118,6 +214,21 @@ LLImageDecodeThread::ImageRequest::~ImageRequest() // Returns true when done, whether or not decode was successful. bool LLImageDecodeThread::ImageRequest::processRequest() +{ + // Image thread pool from CoolVL + + // If not async, decode using this thread + if ((mFlags & FLAG_ASYNC) == 0) + return processRequestIntern(); + + // Try to dispatch to a new thread, if this isn't possible decode on this thread + if (!mQueue->enqueRequest(this)) + return processRequestIntern(); + return true; + // +} + +bool LLImageDecodeThread::ImageRequest::processRequestIntern() { const F32 decode_time_slice = .1f; bool done = true; @@ -172,6 +283,15 @@ bool LLImageDecodeThread::ImageRequest::processRequest() mDecodedAux = done && mDecodedImageAux->getData(); } + // Image thread pool from CoolVL + if (mFlags & FLAG_ASYNC) + { + setStatus(STATUS_COMPLETE); + finishRequest(true); + // always autocomplete + mQueue->completeRequest(mHashKey); + } + // return done; } @@ -191,3 +311,16 @@ bool LLImageDecodeThread::ImageRequest::tut_isOK() { return mResponder.notNull(); } + +bool LLImageDecodeThread::enqueRequest(ImageRequest * req) +{ + for (auto &pThread : mThreadPool) + { + if (!pThread->isBusy()) + { + if( pThread->setRequest(req) ) + return true; + } + } + return false; +} diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h index 1bfb0ddfd3..6104413700 100644 --- a/indra/llimage/llimageworker.h +++ b/indra/llimage/llimageworker.h @@ -31,6 +31,9 @@ #include "llpointer.h" #include "llworkerthread.h" + // Image thread pool +class PoolWorkerThread; + class LLImageDecodeThread : public LLQueuedThread { public: @@ -50,9 +53,10 @@ public: public: ImageRequest(handle_t handle, LLImageFormatted* image, U32 priority, S32 discard, BOOL needs_aux, - LLImageDecodeThread::Responder* responder); + LLImageDecodeThread::Responder* responder, LLImageDecodeThread *aQueue); /*virtual*/ bool processRequest(); + bool processRequestIntern(); /*virtual*/ void finishRequest(bool completed); // Used by unit tests to check the consitency of the request instance @@ -66,13 +70,18 @@ public: // output LLPointer mDecodedImageRaw; LLPointer mDecodedImageAux; + LLImageDecodeThread * mQueue; // Image thread pool from CoolVL BOOL mDecodedRaw; BOOL mDecodedAux; LLPointer mResponder; }; public: - LLImageDecodeThread(bool threaded = true); + // Image thread pool from CoolVL + //LLImageDecodeThread(bool threaded = true); + LLImageDecodeThread(bool threaded = true, U32 aSubThreads = 0 ); + // + virtual ~LLImageDecodeThread(); handle_t decodeImage(LLImageFormatted* image, @@ -99,6 +108,11 @@ private: typedef std::list creation_list_t; creation_list_t mCreationList; LLMutex* mCreationMutex; + + // Image thread pool from CoolVL + std::vector< std::shared_ptr< PoolWorkerThread > > mThreadPool; + bool enqueRequest(ImageRequest*); + // }; #endif diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4511bb5ef8..8085746764 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -25661,5 +25661,16 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 + FSImageDecodeThreads + + Comment + Amount of threads to use for image decoding. 0 = autodetect, 1 = 0ff, >1 number of threads. Needs restart + Persist + 1 + Type + U32 + Value + 1 + diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index fa190721bd..e3d743e792 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2577,8 +2577,12 @@ bool LLAppViewer::initThreads() LLLFSThread::initClass(enable_threads && false); + // Image thread pool from CoolVL + U32 imageThreads = gSavedSettings.getU32("FSImageDecodeThreads"); + // + // Image decoding - LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); + LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true, imageThreads); LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread,