diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 24d01812c9c69805613fb83346d54af0261b5464..fdd5bdfea9780b815c33c3470069312c13b4fac0 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -847,22 +847,24 @@ 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; }
 
-    static void deleteSingleton() { 
-        delete sInstance; 
-        sInstance = nullptr; 
+    static void deleteSingleton() {
+        delete sInstance;
+        sInstance = nullptr;
     }
+
+private:
+    static T* sInstance;
 };
 
 template <class T>
diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp
index e4fa0eccf393fd1a8279ee7cbd1e6fbe3e5bb22b..cf25cc838e1ce55327cbd81b110473d1e2a1b7ef 100644
--- a/indra/llcommon/threadpool.cpp
+++ b/indra/llcommon/threadpool.cpp
@@ -70,6 +70,11 @@ void LL::ThreadPool::close()
 void LL::ThreadPool::run(const std::string& name)
 {
     LL_DEBUGS("ThreadPool") << name << " starting" << LL_ENDL;
-    mQueue.runUntilClose();
+    run();
     LL_DEBUGS("ThreadPool") << name << " stopping" << LL_ENDL;
 }
+
+void LL::ThreadPool::run()
+{
+    mQueue.runUntilClose();
+}
diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h
index 6e3858508bf2d2f7939a0027e033878a407b3ec4..1ca24aec58e9e7052fc5bdedc0ebdeaecd6e3de7 100644
--- a/indra/llcommon/threadpool.h
+++ b/indra/llcommon/threadpool.h
@@ -30,9 +30,25 @@ namespace LL
          * relevant WorkQueue.
          */
         ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024);
-        ~ThreadPool();
+        virtual ~ThreadPool();
+
+        /**
+         * ThreadPool listens for application shutdown messages on the "LLApp"
+         * LLEventPump. Call close() to shut down this ThreadPool early.
+         */
         void close();
 
+        std::string getName() const { return mName; }
+        size_t getWidth() const { return mThreads.size(); }
+        /// obtain a non-const reference to the WorkQueue to post work to it
+        WorkQueue& getQueue() { return mQueue; }
+
+        /**
+         * Override run() if you need special processing. The default run()
+         * implementation simply calls WorkQueue::runUntilClose().
+         */
+        virtual void run();
+
     private:
         void run(const std::string& name);
 
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 71c48801ac47067e84f98d3068fcbd29ef415762..1b6920fe3b6cb1f9eca3cec1a0407a1db1d46ee8 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -172,24 +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();
+    LLImageGLThread::createInstance(window);
 }
 
 //static 
 void LLImageGL::cleanupClass() 
 {
     LL_PROFILE_ZONE_SCOPED;
-    LLImageGLThread::sInstance->mFunctionQueue.close();
-    delete LLImageGLThread::sInstance;
-    LLImageGLThread::sInstance = nullptr;
+    LLImageGLThread::deleteSingleton();
 }
 
 //static
@@ -1532,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");
@@ -2257,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)
 {
     mFinished = false;
 
@@ -2266,9 +2264,11 @@ LLImageGLThread::LLImageGLThread(LLWindow* window)
 
 void LLImageGLThread::run()
 {
+    // We must perform setup on this thread before actually servicing our
+    // 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 b9de481aae9178767ff1e6bd03e15769d790862d..27496def1d62b184731a2cd9312ca78e71a9c197 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 ;
@@ -307,7 +308,7 @@ class LLImageGL : public LLRefCount
 
 };
 
-class LLImageGLThread : public LLThread
+class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool
 {
 public:
     LLImageGLThread(LLWindow* window);
@@ -316,19 +317,15 @@ class LLImageGLThread : public LLThread
     template <typename CALLABLE>
     bool post(CALLABLE&& func)
     {
-        return mFunctionQueue.postIfOpen(std::forward<CALLABLE>(func));
+        return getQueue().postIfOpen(std::forward<CALLABLE>(func));
     }
 
     void run() override;
 
-    // Work Queue for background thread
-    LL::WorkQueue mFunctionQueue;
-
+private:
     LLWindow* mWindow;
     void* mContext;
     LLAtomicBool mFinished;
-
-    static LLImageGLThread* sInstance;
 };
 
 
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 9f3819f7d1222dee45f2ff9728a6a5cde3456559..498e4ef8bc76e76ca26f0b620be84e0e479b81aa 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();
-                LL::WorkQueue::getInstance("mainloop")->post([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 f9f1bfef44c0d5968de3fedfff440cd3c02a309f..4cd4c7cd390c042ab143bfc3953b656f84b2fe93 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 @@ class LLViewerTexture : public LLGLTexture
 	//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;