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
+
+
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,