diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
index 719edcd579e08490ed9f95a685f60efdcaec948b..06e8d8f6094d28417cc1710b121b7250f8bee24d 100644
--- a/indra/llcommon/llthreadsafequeue.h
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -154,6 +154,9 @@ class LLThreadSafeQueue
 	// Returns the size of the queue.
 	size_t size();
 
+    //Returns the capacity of the queue.
+    U32 capacity() { return mCapacity; }
+
 	// closes the queue:
 	// - every subsequent push() call will throw LLThreadSafeQueueInterrupt
 	// - every subsequent tryPush() call will return false
diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
index 5f42fba86602ecf4df3b609ebae06b4ff947495a..33f8dce6ee67148ae805f3a270065bd9e54febd7 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -48,6 +48,7 @@ LLImageDecodeThread::~LLImageDecodeThread()
 // virtual
 S32 LLImageDecodeThread::update(F32 max_time_ms)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LLMutexLock lock(mCreationMutex);
 	for (creation_list_t::iterator iter = mCreationList.begin();
 		 iter != mCreationList.end(); ++iter)
@@ -71,6 +72,7 @@ S32 LLImageDecodeThread::update(F32 max_time_ms)
 LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image, 
 	U32 priority, S32 discard, BOOL needs_aux, Responder* responder)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LLMutexLock lock(mCreationMutex);
 	handle_t handle = generateHandle();
 	mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder));
@@ -118,6 +120,7 @@ LLImageDecodeThread::ImageRequest::~ImageRequest()
 // Returns true when done, whether or not decode was successful.
 bool LLImageDecodeThread::ImageRequest::processRequest()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	const F32 decode_time_slice = .1f;
 	bool done = true;
 	if (!mDecodedRaw && mFormattedImage.notNull())
@@ -164,6 +167,7 @@ bool LLImageDecodeThread::ImageRequest::processRequest()
 
 void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (mResponder.notNull())
 	{
 		bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux);
diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp
index 5947bca670094cec0f160c7fb767c60fbdbe268f..d7f7b2f58e84977e23d3de3cc3618efbb4b4a5ec 100644
--- a/indra/llrender/llcubemap.cpp
+++ b/indra/llrender/llcubemap.cpp
@@ -150,6 +150,7 @@ void LLCubeMap::initRawData(const std::vector<LLPointer<LLImageRaw> >& rawimages
 
 void LLCubeMap::initGLData()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	for (int i = 0; i < 6; i++)
 	{
 		mImages[i]->setSubImage(mRawImages[i], 0, 0, RESOLUTION, RESOLUTION);
@@ -453,6 +454,7 @@ BOOL LLCubeMap::project(F32& v_min, F32& v_max, F32& h_min, F32& h_max,
 
 void LLCubeMap::paintIn(LLVector3 dir[4], const LLColor4U& col)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	F32 v_min, v_max, h_min, h_max;
 	LLVector3 center = dir[0] + dir[1] + dir[2] + dir[3];
 	center.normVec();
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index c41730ebaaacea9d369885fd04380f3821224f93..e18161e53c1d2c0396e4f5753a8ead0b277bd196 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -460,6 +460,7 @@ LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const
 
 LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (mFTFace == NULL)
 		return NULL;
 
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 2fb3b8257dca32b9d005fc49fbcfbfff8a63f808..394fcd2b2f0f93478f1028808f3d5104f40d7869 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -208,6 +208,7 @@ void LLGLSLShader::dumpStats()
 //static
 void LLGLSLShader::startProfile()
 {
+    LL_PROFILE_ZONE_SCOPED;
     if (sProfileEnabled && sCurBoundShaderPtr)
     {
         sCurBoundShaderPtr->placeProfileQuery();
@@ -218,6 +219,7 @@ void LLGLSLShader::startProfile()
 //static
 void LLGLSLShader::stopProfile(U32 count, U32 mode)
 {
+    LL_PROFILE_ZONE_SCOPED;
     if (sProfileEnabled && sCurBoundShaderPtr)
     {
         sCurBoundShaderPtr->readProfileQuery(count, mode);
diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index ad501687ed54fe91f4528a00ab5d41a5ecbbff78..a279e85bae6e01444adb069c1e0128e6758e417a 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -262,6 +262,7 @@ LLTexUnit::eTextureType LLGLTexture::getTarget(void) const
 
 BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	llassert(mGLTexturep.notNull()) ;
 
 	return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height) ;
@@ -269,6 +270,7 @@ BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos,
 
 BOOL LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	llassert(mGLTexturep.notNull()) ;
 
 	return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height) ;
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 276fa55e15a304f74ef4c7aa9587a0e7c84f4d11..aff29bd8577bd622b4856708d8fa534fe3701e9d 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -39,6 +39,7 @@
 #include "llgl.h"
 #include "llglslshader.h"
 #include "llrender.h"
+#include "llwindow.h"
 
 //----------------------------------------------------------------------------
 const F32 MIN_TEXTURE_LIFETIME = 10.f;
@@ -170,15 +171,32 @@ BOOL is_little_endian()
     
 	return (*c == 0x78) ;
 }
+
+LLImageGLThread* LLImageGLThread::sInstance = nullptr;
+
 //static 
-void LLImageGL::initClass(S32 num_catagories, BOOL skip_analyze_alpha /* = false */)
+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();
 }
 
 //static 
 void LLImageGL::cleanupClass() 
-{	
+{
+    LL_PROFILE_ZONE_SCOPED;
+    LLImageGLThread::sInstance->mFunctionQueue.close();
+    delete LLImageGLThread::sInstance;
+    LLImageGLThread::sInstance = nullptr;
 }
 
 //static
@@ -656,6 +674,7 @@ void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_for
 
 void LLImageGL::setImage(const LLImageRaw* imageraw)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	llassert((imageraw->getWidth() == getWidth(mCurrentDiscardLevel)) &&
 			 (imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) &&
 			 (imageraw->getComponents() == getComponents()));
@@ -699,9 +718,8 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
 	}
 	
 	llverify(gGL.getTexUnit(0)->bind(this));
-	
-	
-	if (mUseMipMaps)
+
+    if (mUseMipMaps)
 	{
 		if (data_hasmips)
 		{
@@ -781,7 +799,7 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
 						glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
 					}
 
-					LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
+                    LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
 								 w, h, 
 								 mFormatPrimary, mFormatType,
 								 data_in, mAllowCompression);
@@ -878,7 +896,7 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
 							stop_glerror();
 						}
 
-						LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
+                        LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
 						if (m == 0)
 						{
 							analyzeAlpha(data_in, w, h);
@@ -1067,6 +1085,7 @@ void LLImageGL::postAddToAtlas()
 
 BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (!width || !height)
 	{
 		return TRUE;
@@ -1163,6 +1182,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
 
 BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update);
 }
 
@@ -1201,119 +1221,119 @@ void LLImageGL::deleteTextures(S32 numTextures, U32 *textures)
 
 // static
 static LLTrace::BlockTimerStatHandle FTM_SET_MANUAL_IMAGE("setManualImage");
-void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression)
+void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void* pixels, bool allow_compression)
 {
-	LL_RECORD_BLOCK_TIME(FTM_SET_MANUAL_IMAGE);
-	bool use_scratch = false;
-	U32* scratch = NULL;
-	if (LLRender::sGLCoreProfile)
-	{
-		if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE) 
-		{ //GL_ALPHA is deprecated, convert to RGBA
-			use_scratch = true;
-			scratch = new U32[width*height];
+    LL_RECORD_BLOCK_TIME(FTM_SET_MANUAL_IMAGE);
+    bool use_scratch = false;
+    U32* scratch = NULL;
+    if (LLRender::sGLCoreProfile)
+    {
+        if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)
+        { //GL_ALPHA is deprecated, convert to RGBA
+            use_scratch = true;
+            scratch = new U32[width * height];
 
-			U32 pixel_count = (U32) (width*height);
-			for (U32 i = 0; i < pixel_count; i++)
-			{
-				U8* pix = (U8*) &scratch[i];
-				pix[0] = pix[1] = pix[2] = 0;
-				pix[3] = ((U8*) pixels)[i];
-			}				
-			
-			pixformat = GL_RGBA;
-			intformat = GL_RGBA8;
-		}
+            U32 pixel_count = (U32)(width * height);
+            for (U32 i = 0; i < pixel_count; i++)
+            {
+                U8* pix = (U8*)&scratch[i];
+                pix[0] = pix[1] = pix[2] = 0;
+                pix[3] = ((U8*)pixels)[i];
+            }
 
-		if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE) 
-		{ //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
-			use_scratch = true;
-			scratch = new U32[width*height];
+            pixformat = GL_RGBA;
+            intformat = GL_RGBA8;
+        }
 
-			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];
+        if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)
+        { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
+            use_scratch = true;
+            scratch = new U32[width * height];
 
-				U8* pix = (U8*) &scratch[i];
-				pix[0] = pix[1] = pix[2] = lum;
-				pix[3] = alpha;
-			}				
-			
-			pixformat = GL_RGBA;
-			intformat = GL_RGBA8;
-		}
+            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];
 
-		if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE) 
-		{ //GL_LUMINANCE_ALPHA is deprecated, convert to RGB
-			use_scratch = true;
-			scratch = new U32[width*height];
+                U8* pix = (U8*)&scratch[i];
+                pix[0] = pix[1] = pix[2] = lum;
+                pix[3] = alpha;
+            }
 
-			U32 pixel_count = (U32) (width*height);
-			for (U32 i = 0; i < pixel_count; i++)
-			{
-				U8 lum = ((U8*) pixels)[i];
-				
-				U8* pix = (U8*) &scratch[i];
-				pix[0] = pix[1] = pix[2] = lum;
-				pix[3] = 255;
-			}				
-			
-			pixformat = GL_RGBA;
-			intformat = GL_RGB8;
-		}
-	}
+            pixformat = GL_RGBA;
+            intformat = GL_RGBA8;
+        }
 
-	if (LLImageGL::sCompressTextures && allow_compression)
-	{
-		switch (intformat)
-		{
-			case GL_RGB: 
-			case GL_RGB8:
-				intformat = GL_COMPRESSED_RGB; 
-				break;
-            case GL_SRGB:
-            case GL_SRGB8:
-                intformat = GL_COMPRESSED_SRGB;
-                break;
-			case GL_RGBA:
-			case GL_RGBA8:
-				intformat = GL_COMPRESSED_RGBA; 
-				break;
-            case GL_SRGB_ALPHA:
-            case GL_SRGB8_ALPHA8:
-                intformat = GL_COMPRESSED_SRGB_ALPHA;
-                break;
-			case GL_LUMINANCE:
-			case GL_LUMINANCE8:
-				intformat = GL_COMPRESSED_LUMINANCE;
-				break;
-			case GL_LUMINANCE_ALPHA:
-			case GL_LUMINANCE8_ALPHA8:
-				intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
-				break;
-			case GL_ALPHA:
-			case GL_ALPHA8:
-				intformat = GL_COMPRESSED_ALPHA;
-				break;
-			default:
-				LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL;
-				break;
-		}
-	}
+        if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)
+        { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB
+            use_scratch = true;
+            scratch = new U32[width * height];
 
-	stop_glerror();
-	{
-		LL_PROFILE_ZONE_NAMED("glTexImage2D");
-		glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
-	}
-	stop_glerror();
+            U32 pixel_count = (U32)(width * height);
+            for (U32 i = 0; i < pixel_count; i++)
+            {
+                U8 lum = ((U8*)pixels)[i];
 
-	if (use_scratch)
-	{
-		delete [] scratch;
-	}
+                U8* pix = (U8*)&scratch[i];
+                pix[0] = pix[1] = pix[2] = lum;
+                pix[3] = 255;
+            }
+
+            pixformat = GL_RGBA;
+            intformat = GL_RGB8;
+        }
+    }
+
+    if (LLImageGL::sCompressTextures && allow_compression)
+    {
+        switch (intformat)
+        {
+        case GL_RGB:
+        case GL_RGB8:
+            intformat = GL_COMPRESSED_RGB;
+            break;
+        case GL_SRGB:
+        case GL_SRGB8:
+            intformat = GL_COMPRESSED_SRGB;
+            break;
+        case GL_RGBA:
+        case GL_RGBA8:
+            intformat = GL_COMPRESSED_RGBA;
+            break;
+        case GL_SRGB_ALPHA:
+        case GL_SRGB8_ALPHA8:
+            intformat = GL_COMPRESSED_SRGB_ALPHA;
+            break;
+        case GL_LUMINANCE:
+        case GL_LUMINANCE8:
+            intformat = GL_COMPRESSED_LUMINANCE;
+            break;
+        case GL_LUMINANCE_ALPHA:
+        case GL_LUMINANCE8_ALPHA8:
+            intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
+            break;
+        case GL_ALPHA:
+        case GL_ALPHA8:
+            intformat = GL_COMPRESSED_ALPHA;
+            break;
+        default:
+            LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL;
+            break;
+        }
+    }
+
+    stop_glerror();
+    {
+        LL_PROFILE_ZONE_NAMED("glTexImage2D");
+        glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
+    }
+    stop_glerror();
+
+    if (use_scratch)
+    {
+        delete[] scratch;
+    }
 }
 
 //create an empty GL texture: just create a texture name
@@ -1336,6 +1356,7 @@ BOOL LLImageGL::createGLTexture()
 	if(mTexName)
 	{
 		LLImageGL::deleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ;
+        mTexName = 0;
 	}
 	
 
@@ -1697,7 +1718,7 @@ void LLImageGL::destroyGLTexture()
 			mTextureMemory = (S32Bytes)0;
 		}
 		
-		LLImageGL::deleteTextures(1, &mTexName);			
+		LLImageGL::deleteTextures(1, &mTexName);
 		mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
 		mTexName = 0;		
 		mGLTextureCreated = FALSE ;
@@ -2238,3 +2259,90 @@ void LLImageGL::resetCurTexSizebar()
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL,  nummips);
 */  
+
+LLImageGLThread::LLImageGLThread(LLWindow* window)
+    : LLThread("LLImageGL"), mWindow(window)
+{
+    mFinished = false;
+
+    mContext = mWindow->createSharedContext();
+}
+
+// post a function to be executed on the LLImageGL background thread
+
+bool LLImageGLThread::post(const std::function<void()>& func)
+{
+    try
+    {
+        if (mFunctionQueue.size() < mFunctionQueue.capacity())
+        {
+            //NOTE: tryPushFront will return immediately if the lock is held
+            // desired behavior here is to push and return true unless the 
+            // queue is full or closed
+            mFunctionQueue.pushFront(func);
+        }
+        else
+        {
+            return false;
+        }
+    }
+    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
+    {
+        mCallbackQueue.pushFront(callback);
+    }
+    catch (LLThreadSafeQueueInterrupt e)
+    {
+        //thread is closing, drop request
+        return false;
+    }
+
+    return true;
+}
+
+void LLImageGLThread::executeCallbacks()
+{
+    LL_PROFILE_ZONE_SCOPED;
+    //executed from main thread
+    std::function<void()> callback;
+    while (mCallbackQueue.tryPopBack(callback))
+    {
+        LL_PROFILE_ZONE_NAMED("iglt - callback");
+        callback();
+    }
+}
+
+void LLImageGLThread::run()
+{
+    mWindow->makeContextCurrent(mContext);
+    gGL.init();
+    try
+    {
+        while (true)
+        {
+            LL_PROFILE_ZONE_SCOPED;
+            std::function<void()> curFunc = mFunctionQueue.popBack();
+            {
+                LL_PROFILE_ZONE_NAMED("iglt - function")
+                    curFunc();
+            }
+        }
+    }
+    catch (LLThreadSafeQueueInterrupt e)
+    {
+        //queue is closed, fall out of run loop
+    }
+    gGL.shutdown();
+    mWindow->destroySharedContext(mContext);
+}
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index 61ddc8d59b7e9e63bdc0b0340f6ce68f4f4335be..8e9b483c2d070fbe8f594a63f8eba9c5845694d6 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -35,9 +35,11 @@
 #include "llrefcount.h"
 #include "v2math.h"
 #include "llunits.h"
-
+#include "llthreadsafequeue.h"
 #include "llrender.h"
 class LLTextureAtlas ;
+class LLWindow;
+
 #define BYTES_TO_MEGA_BYTES(x) ((x) >> 20)
 #define MEGA_BYTES_TO_BYTES(x) ((x) << 20)
 
@@ -102,7 +104,7 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
 	void setAllowCompression(bool allow) { mAllowCompression = allow; }
 
 	static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
-
+    
 	BOOL createGLTexture() ;
 	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE,
 		S32 category = sMaxCategories-1);
@@ -265,7 +267,8 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
 #endif
 
 public:
-	static void initClass(S32 num_catagories, BOOL skip_analyze_alpha = false); 
+	static void initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha = false); 
+    static void updateClass();
 	static void cleanupClass() ;
 
 private:
@@ -301,4 +304,30 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
 
 };
 
+class LLImageGLThread : public LLThread
+{
+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();
+
+    void run() override;
+
+    LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
+    LLThreadSafeQueue<std::function<void()>> mCallbackQueue;
+
+    LLWindow* mWindow;
+    void* mContext;
+    LLAtomicBool mFinished;
+
+    static LLImageGLThread* sInstance;
+};
+
+
 #endif // LL_LLIMAGEGL_H
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 43b4441ea8bf7115e652b32a546db34e0f7e2374..b6711e44e32841a15cddc9fb8beb81934a530f91 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -36,7 +36,7 @@
 #include "lltexture.h"
 #include "llshadermgr.h"
 
-LLRender gGL;
+thread_local LLRender gGL;
 
 // Handy copies of last good GL matrices
 F32	gGLModelView[16];
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index af8568f8a3438b45c075b4390f46d05b96dd3201..c08c2d6881eac56c8ba2307ba7ce7a82b0627f2d 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -511,7 +511,7 @@ extern F32 gGLLastProjection[16];
 extern F32 gGLProjection[16];
 extern S32 gGLViewport[4];
 
-extern LLRender gGL;
+extern thread_local LLRender gGL;
 
 // This rotation matrix moves the default OpenGL reference frame 
 // (-Z at, Y up) to Cory's favorite reference frame (X at, Z up)
diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm
index d2c5b11c3d1899ff7b3db870eba8bb53083ab004..b647085b7e66e3f427b1785a02e0566153c87908 100644
--- a/indra/llwindow/llopenglview-objc.mm
+++ b/indra/llwindow/llopenglview-objc.mm
@@ -288,7 +288,8 @@ attributedStringInfo getSegments(NSAttributedString *str)
 	
 	if (vsync)
 	{
-		[glContext setValues:(const GLint*)1 forParameter:NSOpenGLCPSwapInterval];
+		GLint value = 1;
+		[glContext setValues:&value forParameter:NSOpenGLCPSwapInterval];
 	} else {
 		// supress this error after move to Xcode 7:
 		// error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index d4d5b769376d1e4ae70e9333c0ee86adfcadf992..10c0b6a424d1346c4298e7fe00df4c001a45566c 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -78,7 +78,17 @@ class LLWindow : public LLInstanceTracker<LLWindow>
 	BOOL setSize(LLCoordWindow size);
 	virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true);
 	virtual BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) = 0;
-	virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
+
+    //create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread
+    // returns a pointer to be handed back to destroySharedConext/makeContextCurrent
+    virtual void* createSharedContext() = 0;
+    //make the given context current on the current thread
+    virtual void makeContextCurrent(void* context) = 0;
+    //destroy the given context that was retrieved by createSharedContext()
+    //Must be called on the same thread that called createSharedContext()
+    virtual void destroySharedContext(void* context) = 0;
+
+    virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
 	virtual BOOL getCursorPosition(LLCoordWindow *position) = 0;
 	virtual void showCursor() = 0;
 	virtual void hideCursor() = 0;
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
index c692666df140d2203d35c2f2fe8c38b028efda79..a7ae28aa24b23535acd47a19da957fc0b3544073 100644
--- a/indra/llwindow/llwindowheadless.h
+++ b/indra/llwindow/llwindowheadless.h
@@ -49,6 +49,9 @@ class LLWindowHeadless : public LLWindow
 	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;};
 	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;};
 	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;};
+    void* createSharedContext()  { return nullptr; }
+    void makeContextCurrent(void*)  {}
+    void destroySharedContext(void*)  {}
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
 	/*virtual*/ void showCursor() {};
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 0d0607a0bb1e195fa3e651575f3a35ef8abb122f..23830dd24eb687242d2afc09f1f9794011da483f 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1907,6 +1907,34 @@ void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
     allowDirectMarkedTextInput(b, mGLView); // mLanguageTextInputAllowed and mMarkedTextAllowed should be updated at once (by Pell Smit
 }
 
+class sharedContext 
+{
+public:
+    CGLContextObj mContext;
+};
+
+void* LLWindowMacOSX::createSharedContext()
+{
+    sharedContext* sc = new sharedContext();
+    CGLCreateContext(mPixelFormat, mContext, &(sc->mContext));
+
+    return (void *)sc;
+}
+
+void LLWindowMacOSX::makeContextCurrent(void* context)
+{
+    CGLSetCurrentContext(((sharedContext*)context)->mContext);
+}
+
+void LLWindowMacOSX::destroySharedContext(void* context)
+{
+    sharedContext* sc = (sharedContext*)context;
+
+    CGLDestroyContext(sc->mContext);
+
+    delete sc;
+}
+
 void LLWindowMacOSX::interruptLanguageTextInput()
 {
 	commitCurrentPreedit(mGLView);
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index bf45238c8d91bd1150108ee16e88d86faefd0506..ede2b453d5e55fe6bb31d7795d304fb20e49c75d 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -41,85 +41,84 @@
 #undef verify
 #undef require
 
-
 class LLWindowMacOSX : public LLWindow
 {
 public:
-	/*virtual*/ void show();
-	/*virtual*/ void hide();
-	/*virtual*/ void close();
-	/*virtual*/ BOOL getVisible();
-	/*virtual*/ BOOL getMinimized();
-	/*virtual*/ BOOL getMaximized();
-	/*virtual*/ BOOL maximize();
-	/*virtual*/ void minimize();
-	/*virtual*/ void restore();
-	/*virtual*/ BOOL getFullscreen();
-	/*virtual*/ BOOL getPosition(LLCoordScreen *position);
-	/*virtual*/ BOOL getSize(LLCoordScreen *size);
-	/*virtual*/ BOOL getSize(LLCoordWindow *size);
-	/*virtual*/ BOOL setPosition(LLCoordScreen position);
-	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
-	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
-	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
-	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
-	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
-	/*virtual*/ void showCursor();
-	/*virtual*/ void hideCursor();
-	/*virtual*/ void showCursorFromMouseMove();
-	/*virtual*/ void hideCursorUntilMouseMove();
-	/*virtual*/ BOOL isCursorHidden();
-	/*virtual*/ void updateCursor();
-	/*virtual*/ ECursorType getCursor() const;
-	/*virtual*/ void captureMouse();
-	/*virtual*/ void releaseMouse();
-	/*virtual*/ void setMouseClipping( BOOL b );
-	/*virtual*/ BOOL isClipboardTextAvailable();
-	/*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst);
-	/*virtual*/ BOOL copyTextToClipboard(const LLWString & src);
-	/*virtual*/ void flashIcon(F32 seconds);
-	/*virtual*/ F32 getGamma();
-	/*virtual*/ BOOL setGamma(const F32 gamma); // Set the gamma
-	/*virtual*/ U32 getFSAASamples();
-	/*virtual*/ void setFSAASamples(const U32 fsaa_samples);
-	/*virtual*/ BOOL restoreGamma();			// Restore original gamma table (before updating gamma)
-	/*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
-	/*virtual*/ void gatherInput();
-	/*virtual*/ void delayInputProcessing() {};
-	/*virtual*/ void swapBuffers();
+	void show() override;
+	void hide() override;
+	void close() override;
+	BOOL getVisible() override;
+	BOOL getMinimized() override;
+	BOOL getMaximized() override;
+	BOOL maximize() override;
+	void minimize() override;
+	void restore() override;
+	BOOL getFullscreen();
+	BOOL getPosition(LLCoordScreen *position) override;
+	BOOL getSize(LLCoordScreen *size) override;
+	BOOL getSize(LLCoordWindow *size) override;
+	BOOL setPosition(LLCoordScreen position) override;
+	BOOL setSizeImpl(LLCoordScreen size) override;
+	BOOL setSizeImpl(LLCoordWindow size) override;
+	BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) override;
+	BOOL setCursorPosition(LLCoordWindow position) override;
+	BOOL getCursorPosition(LLCoordWindow *position) override;
+	void showCursor() override;
+	void hideCursor() override;
+	void showCursorFromMouseMove() override;
+	void hideCursorUntilMouseMove() override;
+	BOOL isCursorHidden() override;
+	void updateCursor() override;
+	ECursorType getCursor() const override;
+	void captureMouse() override;
+	void releaseMouse() override;
+	void setMouseClipping( BOOL b ) override;
+	BOOL isClipboardTextAvailable() override;
+	BOOL pasteTextFromClipboard(LLWString &dst) override;
+	BOOL copyTextToClipboard(const LLWString & src) override;
+	void flashIcon(F32 seconds) override;
+	F32 getGamma() override;
+	BOOL setGamma(const F32 gamma) override; // Set the gamma
+	U32 getFSAASamples() override;
+	void setFSAASamples(const U32 fsaa_samples) override;
+	BOOL restoreGamma() override;			// Restore original gamma table (before updating gamma)
+	ESwapMethod getSwapMethod() override { return mSwapMethod; }
+	void gatherInput() override;
+	void delayInputProcessing() override {};
+	void swapBuffers() override;
 	
 	// handy coordinate space conversion routines
-	/*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to);
-	/*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to);
-	/*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to);
-	/*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to);
-	/*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to);
-	/*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to);
+	BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to) override;
+	BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to) override;
+	BOOL convertCoords(LLCoordWindow from, LLCoordGL *to) override;
+	BOOL convertCoords(LLCoordGL from, LLCoordWindow *to) override;
+	BOOL convertCoords(LLCoordScreen from, LLCoordGL *to) override;
+	BOOL convertCoords(LLCoordGL from, LLCoordScreen *to) override;
 
-	/*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions);
-	/*virtual*/ F32	getNativeAspectRatio();
-	/*virtual*/ F32 getPixelAspectRatio();
-	/*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
+	LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override;
+	F32	getNativeAspectRatio() override;
+	F32 getPixelAspectRatio() override;
+	void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; }
 
-	/*virtual*/ void beforeDialog();
-	/*virtual*/ void afterDialog();
+	void beforeDialog() override;
+	void afterDialog() override;
 
-	/*virtual*/ BOOL dialogColorPicker(F32 *r, F32 *g, F32 *b);
+	BOOL dialogColorPicker(F32 *r, F32 *g, F32 *b) override;
 
-	/*virtual*/ void *getPlatformWindow();
-	/*virtual*/ void bringToFront() {};
+	void *getPlatformWindow() override;
+	void bringToFront() override {};
 	
-	/*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b);
-	/*virtual*/ void interruptLanguageTextInput();
-	/*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async);
-	/*virtual*/ F32 getSystemUISize();
+	void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b) override;
+	void interruptLanguageTextInput() override;
+	void spawnWebBrowser(const std::string& escaped_url, bool async) override;
+	F32 getSystemUISize() override;
 
 	static std::vector<std::string> getDisplaysResolutionList();
 
 	static std::vector<std::string> getDynamicFallbackFontList();
 
 	// Provide native key event data
-	/*virtual*/ LLSD getNativeKeyData();
+	LLSD getNativeKeyData() override;
 	
 	void* getWindow() { return mWindow; }
 	LLWindowCallbacks* getCallbacks() { return mCallbacks; }
@@ -132,6 +131,15 @@ class LLWindowMacOSX : public LLWindow
     
     bool allowsLanguageInput() { return mLanguageTextInputAllowed; }
 
+    //create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread
+    // returns a pointer to be handed back to destroySharedConext/makeContextCurrent
+    void* createSharedContext() override;
+    //make the given context current on the current thread
+    void makeContextCurrent(void* context) override;
+    //destroy the given context that was retrieved by createSharedContext()
+    //Must be called on the same thread that called createSharedContext()
+    void destroySharedContext(void* context) override;
+
 protected:
 	LLWindowMacOSX(LLWindowCallbacks* callbacks,
 		const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags,
@@ -141,7 +149,7 @@ class LLWindowMacOSX : public LLWindow
 		~LLWindowMacOSX();
 
 	void	initCursors();
-	BOOL	isValid();
+	BOOL	isValid() override;
 	void	moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
 
 
@@ -157,7 +165,7 @@ class LLWindowMacOSX : public LLWindow
 	BOOL	shouldPostQuit() { return mPostQuit; }
     
     //Satisfy MAINT-3135 and MAINT-3288 with a flag.
-    /*virtual */ void setOldResize(bool oldresize) {setResizeMode(oldresize, mGLView); }
+    /*virtual */ void setOldResize(bool oldresize) override {setResizeMode(oldresize, mGLView); }
 
 private:
     void restoreGLContext();
@@ -231,9 +239,9 @@ class LLSplashScreenMacOSX : public LLSplashScreen
 	LLSplashScreenMacOSX();
 	virtual ~LLSplashScreenMacOSX();
 
-	/*virtual*/ void showImpl();
-	/*virtual*/ void updateImpl(const std::string& mesg);
-	/*virtual*/ void hideImpl();
+	void showImpl();
+	void updateImpl(const std::string& mesg);
+	void hideImpl();
 
 private:
 	WindowRef   mWindow;
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 4ce7c30bef172bc06d449a184e377a936fbfecb8..12d4c6c30eed977b2347aa5f998a8553a2e7d964 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -1774,58 +1774,11 @@ const	S32   max_format  = (S32)num_formats - 1;
 	mhRC = 0;
 	if (wglCreateContextAttribsARB)
 	{ //attempt to create a specific versioned context
-		S32 attribs[] = 
-		{ //start at 4.2
-			WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
-			WGL_CONTEXT_MINOR_VERSION_ARB, 2,
-			WGL_CONTEXT_PROFILE_MASK_ARB,  LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
-			WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
-			0
-		};
-
-		bool done = false;
-		while (!done)
-		{
-			mhRC = wglCreateContextAttribsARB(mhDC, mhRC, attribs);
-
-			if (!mhRC)
-			{
-				if (attribs[3] > 0)
-				{ //decrement minor version
-					attribs[3]--;
-				}
-				else if (attribs[1] > 3)
-				{ //decrement major version and start minor version over at 3
-					attribs[1]--;
-					attribs[3] = 3;
-				}
-				else
-				{ //we reached 3.0 and still failed, bail out
-					done = true;
-				}
-			}
-			else
-			{
-				LL_INFOS() << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << 
-					(LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << LL_ENDL;
-				done = true;
-
-			// force sNoFixedFunction iff we're trying to use nsight debugging which does not support many legacy API uses
-
-				// nSight doesn't support use of legacy API funcs in the fixed function pipe
-				if (LLRender::sGLCoreProfile || LLRender::sNsightDebugSupport)
-				{
-					LLGLSLShader::sNoFixedFunction = true;
-				}
-			}
-		}
-	}
-
-	if (!mhRC && !(mhRC = wglCreateContext(mhDC)))
-	{
-		close();
-		OSMessageBox(mCallbacks->translateString("MBGLContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
-		return FALSE;
+        mhRC = (HGLRC) createSharedContext();
+        if (!mhRC)
+        {
+            return FALSE;
+        }
 	}
 
 	if (!wglMakeCurrent(mhDC, mhRC))
@@ -1880,6 +1833,75 @@ const	S32   max_format  = (S32)num_formats - 1;
 	return TRUE;
 }
 
+void* LLWindowWin32::createSharedContext()
+{
+    S32 attribs[] =
+    {
+        WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
+        WGL_CONTEXT_MINOR_VERSION_ARB, 2,
+        WGL_CONTEXT_PROFILE_MASK_ARB,  LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
+        WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
+        0
+    };
+
+    HGLRC rc = wglCreateContextAttribsARB(mhDC, mhRC, attribs);
+
+    bool done = false;
+    while (!done)
+    {
+        rc = wglCreateContextAttribsARB(mhDC, mhRC, attribs);
+
+        if (!rc)
+        {
+            if (attribs[3] > 0)
+            { //decrement minor version
+                attribs[3]--;
+            }
+            else if (attribs[1] > 3)
+            { //decrement major version and start minor version over at 3
+                attribs[1]--;
+                attribs[3] = 3;
+            }
+            else
+            { //we reached 3.0 and still failed, bail out
+                done = true;
+            }
+        }
+        else
+        {
+            LL_INFOS() << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) <<
+                (LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << LL_ENDL;
+            done = true;
+
+            // force sNoFixedFunction iff we're trying to use nsight debugging which does not support many legacy API uses
+
+                // nSight doesn't support use of legacy API funcs in the fixed function pipe
+            if (LLRender::sGLCoreProfile || LLRender::sNsightDebugSupport)
+            {
+                LLGLSLShader::sNoFixedFunction = true;
+            }
+        }
+    }
+
+    if (!rc && !(rc = wglCreateContext(mhDC)))
+    {
+        close();
+        OSMessageBox(mCallbacks->translateString("MBGLContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
+    }
+
+    return rc;
+}
+
+void LLWindowWin32::makeContextCurrent(void* contextPtr)
+{
+    wglMakeCurrent(mhDC, (HGLRC) contextPtr);
+}
+
+void LLWindowWin32::destroySharedContext(void* contextPtr)
+{
+    wglDeleteContext((HGLRC)contextPtr);
+}
+
 void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScreen& size )
 {
 	if( mIsMouseClipping )
@@ -3720,6 +3742,7 @@ BOOL LLWindowWin32::resetDisplayResolution()
 
 void LLWindowWin32::swapBuffers()
 {
+    LL_PROFILE_ZONE_SCOPED;
     ASSERT_MAIN_THREAD();
 	SwapBuffers(mhDC);
 
@@ -4731,4 +4754,4 @@ void LLWindowWin32::post(const std::function<void()>& func)
 #else
     mFunctionQueue.pushFront(func);
 #endif
-}
\ No newline at end of file
+}
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 66647459b2edbb2c2631b8e979a8a2cc714b1d76..5f253b5df33515e3d0f7cb8b9c901b314df0833e 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -93,6 +93,9 @@ class LLWindowWin32 : public LLWindow
 	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
 	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
 	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
+    void* createSharedContext() override;
+    void makeContextCurrent(void* context) override;
+    void destroySharedContext(void* context) override;
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
 	/*virtual*/ void showCursor();
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 05c3fc3bfe8f983e880b360cc63b56f7fe22b8d3..d05d100ddf32617fec9cf820b2cf86d9c5ba99c3 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9814,7 +9814,7 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
-      <integer>0</integer>
+      <integer>1</integer>
     </map>
     <key>RenderGlow</key>
     <map>
@@ -10599,7 +10599,7 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
-      <integer>0</integer>
+      <integer>1</integer>
     </map>
   <key>RenderUseTransformFeedback</key>
   <map>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 722a6caa65aecfe467dc12bbb79d0724be459a8f..e46e8feb14e3a984086355614bf116f43fc7d309 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -613,7 +613,7 @@ static void settings_modify()
 	LLPipeline::sRenderDeferred		= LLPipeline::sRenderTransparentWater && LLPipeline::sRenderBump && gSavedSettings.getBOOL("RenderDeferred");
 	LLVOSurfacePatch::sLODFactor		= gSavedSettings.getF32("RenderTerrainLODFactor");
 	LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4]
-	gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession;
+    gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession;
 	gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline");
 }
 
@@ -4831,6 +4831,7 @@ 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/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 112da556825647b5d5d1ed65cd2119c8f576b1e2..111b45612e6e88d4bf118e7c05b15a58a922a69a 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -147,6 +147,7 @@ void LLNetMap::setScale( F32 scale )
 
 void LLNetMap::draw()
 {
+    LL_PROFILE_ZONE_SCOPED;
  	static LLFrameTimer map_timer;
 	static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white);
 	static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white);
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index f64db7beb54e73e77f43f0279aa74cacb74560d2..63e561147df90ff9a3ea2b3e9ce3bb244fca5d04 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -314,6 +314,7 @@ class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler
 		// Threads:  Ttc
 		virtual void completed(bool success)
 		{
+            LL_PROFILE_ZONE_SCOPED;
 			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 			if (worker)
 			{
@@ -338,6 +339,7 @@ class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler
 		// Threads:  Ttc
 		virtual void completed(bool success)
 		{
+            LL_PROFILE_ZONE_SCOPED;
 			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 			if (worker)
 			{
@@ -362,6 +364,7 @@ class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler
 		// Threads:  Tid
 		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
 		{
+            LL_PROFILE_ZONE_SCOPED;
 			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 			if (worker)
 			{
@@ -1139,6 +1142,7 @@ void LLTextureFetchWorker::startWork(S32 param)
 // Threads:  Ttf
 bool LLTextureFetchWorker::doWork(S32 param)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND);						// 404
 	static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE);		// 503
 	static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);	// 416;
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index af55f68cd2af192c367a1f331632f8c01a81a860..274f53a1603c0fbf3a5ef65a084132750bd285ba 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -184,6 +184,7 @@ void LLViewerTextureManager::findFetchedTextures(const LLUUID& id, std::vector<L
 
 void  LLViewerTextureManager::findTextures(const LLUUID& id, std::vector<LLViewerTexture*> &output)
 {
+    LL_PROFILE_ZONE_SCOPED;
     std::vector<LLViewerFetchedTexture*> fetched_output;
     gTextureList.findTexturesByID(id, fetched_output);
     std::vector<LLViewerFetchedTexture*>::iterator iter = fetched_output.begin();
@@ -485,6 +486,7 @@ static LLTrace::BlockTimerStatHandle FTM_TEXTURE_MEMORY_CHECK("Memory Check");
 //static 
 bool LLViewerTexture::isMemoryForTextureLow()
 {
+    LL_PROFILE_ZONE_SCOPED;
     // Note: we need to figure out a better source for 'min' values,
     // what is free for low end at minimal settings is 'nothing left'
     // for higher end gpus at high settings.
@@ -501,6 +503,7 @@ bool LLViewerTexture::isMemoryForTextureLow()
 //static
 bool LLViewerTexture::isMemoryForTextureSuficientlyFree()
 {
+    LL_PROFILE_ZONE_SCOPED;
     const S32Megabytes DESIRED_FREE_TEXTURE_MEMORY(50);
     const S32Megabytes DESIRED_FREE_MAIN_MEMORY(200);
 
@@ -514,6 +517,7 @@ bool LLViewerTexture::isMemoryForTextureSuficientlyFree()
 //static
 void LLViewerTexture::getGPUMemoryForTextures(S32Megabytes &gpu, S32Megabytes &physical)
 {
+    LL_PROFILE_ZONE_SCOPED;
     static LLFrameTimer timer;
     static S32Megabytes gpu_res = S32Megabytes(S32_MAX);
     static S32Megabytes physical_res = S32Megabytes(S32_MAX);
@@ -526,27 +530,29 @@ void LLViewerTexture::getGPUMemoryForTextures(S32Megabytes &gpu, S32Megabytes &p
     }
     timer.reset();
 
-    LL_RECORD_BLOCK_TIME(FTM_TEXTURE_MEMORY_CHECK);
-
-    if (gGLManager.mHasATIMemInfo)
     {
-        S32 meminfo[4];
-        glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
-        gpu_res = (S32Megabytes)meminfo[0];
+        LL_RECORD_BLOCK_TIME(FTM_TEXTURE_MEMORY_CHECK);
 
-        //check main memory, only works for windows.
-        LLMemory::updateMemoryInfo();
-        physical_res = LLMemory::getAvailableMemKB();
-    }
-    else if (gGLManager.mHasNVXMemInfo)
-    {
-        S32 free_memory;
-        glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory);
-        gpu_res = (S32Megabytes)(free_memory / 1024);
-    }
+        if (gGLManager.mHasATIMemInfo)
+        {
+            S32 meminfo[4];
+            glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
+            gpu_res = (S32Megabytes)meminfo[0];
 
-    gpu = gpu_res;
-    physical = physical_res;
+            //check main memory, only works for windows.
+            LLMemory::updateMemoryInfo();
+            physical_res = LLMemory::getAvailableMemKB();
+        }
+        else if (gGLManager.mHasNVXMemInfo)
+        {
+            S32 free_memory;
+            glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory);
+            gpu_res = (S32Megabytes)(free_memory / 1024);
+        }
+
+        gpu = gpu_res;
+        physical = physical_res;
+    }
 }
 
 static LLTrace::BlockTimerStatHandle FTM_TEXTURE_UPDATE_MEDIA("Media");
@@ -555,6 +561,7 @@ static LLTrace::BlockTimerStatHandle FTM_TEXTURE_UPDATE_TEST("Test");
 //static
 void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	sCurrentTime = gFrameTimeSeconds;
 
 	LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
@@ -704,6 +711,7 @@ void LLViewerTexture::cleanup()
 
 void LLViewerTexture::notifyAboutCreatingTexture()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	for(U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
 	{
 		for(U32 f = 0; f < mNumFaces[ch]; f++)
@@ -715,6 +723,7 @@ void LLViewerTexture::notifyAboutCreatingTexture()
 
 void LLViewerTexture::notifyAboutMissingAsset()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	for(U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
 	{
 		for(U32 f = 0; f < mNumFaces[ch]; f++)
@@ -727,6 +736,7 @@ void LLViewerTexture::notifyAboutMissingAsset()
 // virtual
 void LLViewerTexture::dump()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LLGLTexture::dump();
 
 	LL_INFOS() << "LLViewerTexture"
@@ -762,6 +772,7 @@ bool LLViewerTexture::isActiveFetching()
 
 bool LLViewerTexture::bindDebugImage(const S32 stage)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (stage < 0) return false;
 
 	bool res = true;
@@ -780,6 +791,7 @@ bool LLViewerTexture::bindDebugImage(const S32 stage)
 
 bool LLViewerTexture::bindDefaultImage(S32 stage) 
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (stage < 0) return false;
 
 	bool res = true;
@@ -822,6 +834,7 @@ void LLViewerTexture::forceImmediateUpdate()
 
 void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) const 
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(needs_gltexture)
 	{
 		mNeedsGLTexture = TRUE;
@@ -864,6 +877,7 @@ void LLViewerTexture::setKnownDrawSize(S32 width, S32 height)
 //virtual
 void LLViewerTexture::addFace(U32 ch, LLFace* facep) 
 {
+    LL_PROFILE_ZONE_SCOPED;
 	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
 
 	if(mNumFaces[ch] >= mFaceList[ch].size())
@@ -879,6 +893,7 @@ void LLViewerTexture::addFace(U32 ch, LLFace* facep)
 //virtual
 void LLViewerTexture::removeFace(U32 ch, LLFace* facep) 
 {
+    LL_PROFILE_ZONE_SCOPED;
 	llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
 
 	if(mNumFaces[ch] > 1)
@@ -919,6 +934,7 @@ S32 LLViewerTexture::getNumFaces(U32 ch) const
 //virtual
 void LLViewerTexture::addVolume(U32 ch, LLVOVolume* volumep)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (mNumVolumes[ch] >= mVolumeList[ch].size())
 	{
 		mVolumeList[ch].resize(2 * mNumVolumes[ch] + 1);
@@ -932,6 +948,7 @@ void LLViewerTexture::addVolume(U32 ch, LLVOVolume* volumep)
 //virtual
 void LLViewerTexture::removeVolume(U32 ch, LLVOVolume* volumep)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (mNumVolumes[ch] > 1)
 	{
 		S32 index = volumep->getIndexInTex(ch); 
@@ -955,6 +972,7 @@ S32 LLViewerTexture::getNumVolumes(U32 ch) const
 
 void LLViewerTexture::reorganizeFaceList()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	static const F32 MAX_WAIT_TIME = 20.f; // seconds
 	static const U32 MAX_EXTRA_BUFFER_SIZE = 4;
 
@@ -978,6 +996,7 @@ void LLViewerTexture::reorganizeFaceList()
 
 void LLViewerTexture::reorganizeVolumeList()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	static const F32 MAX_WAIT_TIME = 20.f; // seconds
 	static const U32 MAX_EXTRA_BUFFER_SIZE = 4;
 
@@ -1180,6 +1199,7 @@ FTType LLViewerFetchedTexture::getFTType() const
 
 void LLViewerFetchedTexture::cleanup()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
 		iter != mLoadedCallbackList.end(); )
 	{
@@ -1205,6 +1225,7 @@ void LLViewerFetchedTexture::cleanup()
 //access the fast cache
 void LLViewerFetchedTexture::loadFromFastCache()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!mInFastCacheList)
 	{
 		return; //no need to access the fast cache.
@@ -1350,6 +1371,7 @@ void LLViewerFetchedTexture::dump()
 // ONLY called from LLViewerFetchedTextureList
 void LLViewerFetchedTexture::destroyTexture() 
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(LLImageGL::sGlobalTextureMemory < sMaxDesiredTextureMem * 0.95f)//not ready to release unused memory.
 	{
 		return ;
@@ -1366,6 +1388,7 @@ void LLViewerFetchedTexture::destroyTexture()
 
 void LLViewerFetchedTexture::addToCreateTexture()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	bool force_update = false;
 	if (getComponents() != mRawImage->getComponents())
 	{
@@ -1407,6 +1430,7 @@ void LLViewerFetchedTexture::addToCreateTexture()
 	}
 	else
 	{	
+        LL_PROFILE_ZONE_SCOPED;
 #if 1
 		//
 		//if mRequestedDiscardLevel > mDesiredDiscardLevel, we assume the required image res keep going up,
@@ -1451,99 +1475,100 @@ void LLViewerFetchedTexture::addToCreateTexture()
 			}
 		}
 #endif
-		mNeedsCreateTexture = TRUE;
-		gTextureList.mCreateTextureList.insert(this);
-	}	
+        scheduleCreateTexture();
+	}
 	return;
 }
 
 // ONLY called from LLViewerTextureList
-BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
+BOOL LLViewerFetchedTexture::preCreateTexture(S32 usename/*= 0*/)
 {
-	if (!mNeedsCreateTexture)
-	{
-		destroyRawImage();
-		return FALSE;
-	}
-	mNeedsCreateTexture = FALSE;
-	if (mRawImage.isNull())
-	{
-		LL_ERRS() << "LLViewerTexture trying to create texture with no Raw Image" << LL_ENDL;
-	}
-	if (mRawImage->isBufferInvalid())
-	{
-		LL_WARNS() << "Can't create a texture: invalid image data" << LL_ENDL;
-		destroyRawImage();
-		return FALSE;
-	}
-// 	LL_INFOS() << llformat("IMAGE Creating (%d) [%d x %d] Bytes: %d ",
-// 						mRawDiscardLevel, 
-// 						mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize())
-// 			<< mID.getString() << LL_ENDL;
-	BOOL res = TRUE;
+    LL_PROFILE_ZONE_SCOPED;
+    if (!mNeedsCreateTexture)
+    {
+        destroyRawImage();
+        return FALSE;
+    }
+    mNeedsCreateTexture = FALSE;
 
-	// store original size only for locally-sourced images
-	if (mUrl.compare(0, 7, "file://") == 0)
-	{
-		mOrigWidth = mRawImage->getWidth();
-		mOrigHeight = mRawImage->getHeight();
+    if (mRawImage.isNull())
+    {
+        LL_ERRS() << "LLViewerTexture trying to create texture with no Raw Image" << LL_ENDL;
+    }
+    if (mRawImage->isBufferInvalid())
+    {
+        LL_WARNS() << "Can't create a texture: invalid image data" << LL_ENDL;
+        destroyRawImage();
+        return FALSE;
+    }
+    // 	LL_INFOS() << llformat("IMAGE Creating (%d) [%d x %d] Bytes: %d ",
+    // 						mRawDiscardLevel, 
+    // 						mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize())
+    // 			<< mID.getString() << LL_ENDL;
+    BOOL res = TRUE;
+
+    // store original size only for locally-sourced images
+    if (mUrl.compare(0, 7, "file://") == 0)
+    {
+        mOrigWidth = mRawImage->getWidth();
+        mOrigHeight = mRawImage->getHeight();
 
         // This is only safe because it's a local image and fetcher doesn't use raw data
         // from local images, but this might become unsafe in case of changes to fetcher
-		if (mBoostLevel == BOOST_PREVIEW)
-		{ 
-			mRawImage->biasedScaleToPowerOfTwo(1024);
-		}
-		else
-		{ // leave black border, do not scale image content
-			mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE);
-		}
-		
-		mFullWidth = mRawImage->getWidth();
-		mFullHeight = mRawImage->getHeight();
-		setTexelsPerImage();
-	}
-	else
-	{
-		mOrigWidth = mFullWidth;
-		mOrigHeight = mFullHeight;
-	}
+        if (mBoostLevel == BOOST_PREVIEW)
+        {
+            mRawImage->biasedScaleToPowerOfTwo(1024);
+        }
+        else
+        { // leave black border, do not scale image content
+            mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE);
+        }
 
-	bool size_okay = true;
+        mFullWidth = mRawImage->getWidth();
+        mFullHeight = mRawImage->getHeight();
+        setTexelsPerImage();
+    }
+    else
+    {
+        mOrigWidth = mFullWidth;
+        mOrigHeight = mFullHeight;
+    }
 
-	S32 discard_level = mRawDiscardLevel;
-	if (mRawDiscardLevel < 0)
-	{
-		LL_DEBUGS() << "Negative raw discard level when creating image: " << mRawDiscardLevel << LL_ENDL;
-		discard_level = 0;
-	}
+    bool size_okay = true;
 
-	U32 raw_width = mRawImage->getWidth() << discard_level;
-	U32 raw_height = mRawImage->getHeight() << discard_level;
+    S32 discard_level = mRawDiscardLevel;
+    if (mRawDiscardLevel < 0)
+    {
+        LL_DEBUGS() << "Negative raw discard level when creating image: " << mRawDiscardLevel << LL_ENDL;
+        discard_level = 0;
+    }
 
-	if( raw_width > MAX_IMAGE_SIZE || raw_height > MAX_IMAGE_SIZE )
-	{
-		LL_INFOS() << "Width or height is greater than " << MAX_IMAGE_SIZE << ": (" << raw_width << "," << raw_height << ")" << LL_ENDL;
-		size_okay = false;
-	}
-	
-	if (!LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight()))
-	{
-		// A non power-of-two image was uploaded (through a non standard client)
-		LL_INFOS() << "Non power of two width or height: (" << mRawImage->getWidth() << "," << mRawImage->getHeight() << ")" << LL_ENDL;
-		size_okay = false;
-	}
-	
-	if( !size_okay )
-	{
-		// An inappropriately-sized image was uploaded (through a non standard client)
-		// We treat these images as missing assets which causes them to
-		// be renderd as 'missing image' and to stop requesting data
-		LL_WARNS() << "!size_ok, setting as missing" << LL_ENDL;
-		setIsMissingAsset();
-		destroyRawImage();
-		return FALSE;
-	}
+    U32 raw_width = mRawImage->getWidth() << discard_level;
+    U32 raw_height = mRawImage->getHeight() << discard_level;
+
+    if (raw_width > MAX_IMAGE_SIZE || raw_height > MAX_IMAGE_SIZE)
+    {
+        LL_INFOS() << "Width or height is greater than " << MAX_IMAGE_SIZE << ": (" << raw_width << "," << raw_height << ")" << LL_ENDL;
+        size_okay = false;
+    }
+
+    if (!LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight()))
+    {
+        // A non power-of-two image was uploaded (through a non standard client)
+        LL_INFOS() << "Non power of two width or height: (" << mRawImage->getWidth() << "," << mRawImage->getHeight() << ")" << LL_ENDL;
+        size_okay = false;
+    }
+
+    if (!size_okay)
+    {
+        // An inappropriately-sized image was uploaded (through a non standard client)
+        // We treat these images as missing assets which causes them to
+        // be renderd as 'missing image' and to stop requesting data
+        LL_WARNS() << "!size_ok, setting as missing" << LL_ENDL;
+        setIsMissingAsset();
+        destroyRawImage();
+        return FALSE;
+    }
 
     if (mGLTexturep->getHasExplicitFormat())
     {
@@ -1565,19 +1590,79 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
         }
     }
 
-	res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
+    return res;
+}
 
-	notifyAboutCreatingTexture();
+BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
+{
+    if (!mNeedsCreateTexture)
+    {
+        return FALSE;
+    }
 
-	setActive();
+	BOOL res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
+    
+	return res;
+}
 
-	if (!needsToSaveRawImage())
-	{
-		mNeedsAux = FALSE;
-		destroyRawImage();
-	}
+void LLViewerFetchedTexture::postCreateTexture()
+{
+    if (!mNeedsCreateTexture)
+    {
+        return;
+    }
 
-	return res;
+    notifyAboutCreatingTexture();
+
+    setActive();
+
+    if (!needsToSaveRawImage())
+    {
+        mNeedsAux = FALSE;
+        destroyRawImage();
+    }
+
+    mNeedsCreateTexture = FALSE;
+}
+
+void LLViewerFetchedTexture::scheduleCreateTexture()
+{
+    ref();
+    mNeedsCreateTexture = TRUE;
+    if (preCreateTexture())
+    {
+        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_PROFILE_ZONE_NAMED("iglt - sync");
+                    if (gGLManager.mHasSync)
+                    {
+                        auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+                        glClientWaitSync(sync, 0, 0);
+                        glDeleteSync(sync);
+                    }
+                    else
+                    {
+                        glFinish();
+                    }
+                }
+                LLImageGLThread::sInstance->postCallback([this]()
+                    {
+                        //finalize on main thread
+                        postCreateTexture();
+                        unref();
+                    });
+            }))
+#endif
+        {
+            gTextureList.mCreateTextureList.insert(this);
+            unref();
+        }
+    }
 }
 
 // Call with 0,0 to turn this feature off.
@@ -1869,6 +1954,7 @@ void LLViewerFetchedTexture::setAdditionalDecodePriority(F32 priority)
 
 void LLViewerFetchedTexture::updateVirtualSize() 
 {	
+    LL_PROFILE_ZONE_SCOPED;
 	if(!mMaxVirtualSizeResetCounter)
 	{
 		addTextureStats(0.f, FALSE);//reset
@@ -1960,6 +2046,7 @@ bool LLViewerFetchedTexture::isActiveFetching()
 
 bool LLViewerFetchedTexture::updateFetch()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled", false);
 	static LLCachedControl<F32>  sCameraMotionThreshold(gSavedSettings,"TextureCameraMotionThreshold", 0.2);
 	static LLCachedControl<S32>  sCameraMotionBoost(gSavedSettings,"TextureCameraMotionBoost", 3);
@@ -2062,7 +2149,7 @@ bool LLViewerFetchedTexture::updateFetch()
 				}
 				else
 				{
-					mIsRawImageValid = TRUE;			
+					mIsRawImageValid = TRUE;
 					addToCreateTexture();
 				}
 
@@ -2890,6 +2977,7 @@ void LLViewerFetchedTexture::destroyRawImage()
 //virtual
 void LLViewerFetchedTexture::switchToCachedImage()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(mCachedRawImage.notNull())
 	{
 		mRawImage = mCachedRawImage;
@@ -2901,12 +2989,12 @@ void LLViewerFetchedTexture::switchToCachedImage()
 			mComponents = mRawImage->getComponents();
 			mGLTexturep->setComponents(mComponents);
 			gTextureList.dirtyImage(this);
-		}			
+		}
 
 		mIsRawImageValid = TRUE;
 		mRawDiscardLevel = mCachedRawDiscardLevel;
-		gTextureList.mCreateTextureList.insert(this);
-		mNeedsCreateTexture = TRUE;		
+
+        scheduleCreateTexture();
 	}
 }
 
@@ -3180,6 +3268,7 @@ bool LLViewerLODTexture::isUpdateFrozen()
 //virtual
 void LLViewerLODTexture::processTextureStats()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	updateVirtualSize();
 	
 	static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes", false);
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 69568cc8255cadf45d160a1298b00eefc126ca03..a5a1fb2c16cb8abd81d29f09094dd9dce24ba993 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -321,9 +321,13 @@ class LLViewerFetchedTexture : public LLViewerTexture
 
 	void addToCreateTexture();
 
-
-	 // ONLY call from LLViewerTextureList
+    //call to determine if createTexture is necessary
+    BOOL preCreateTexture(S32 usename = 0);
+	 // ONLY call from LLViewerTextureList or ImageGL background thread
 	BOOL createTexture(S32 usename = 0);
+    void postCreateTexture();
+    void scheduleCreateTexture();
+
 	void destroyTexture() ;
 
 	virtual void processTextureStats() ;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 12495078e99c9ea8d1874364a01b618bd3886096..db740b69e9edaf1e13aff846f0806550f2928309 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -114,6 +114,7 @@ void LLViewerTextureList::init()
 
 void LLViewerTextureList::doPreloadImages()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LL_DEBUGS("ViewerImages") << "Preloading images..." << LL_ENDL;
 	
 	llassert_always(mInitialized) ;
@@ -205,6 +206,7 @@ static std::string get_texture_list_name()
 
 void LLViewerTextureList::doPrefetchImages()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (LLAppViewer::instance()->getPurgeCache())
 	{
 		// cache was purged, no point
@@ -258,6 +260,7 @@ LLViewerTextureList::~LLViewerTextureList()
 
 void LLViewerTextureList::shutdown()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// clear out preloads
 	mImagePreloads.clear();
 
@@ -333,6 +336,7 @@ void LLViewerTextureList::shutdown()
 
 void LLViewerTextureList::dump()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LL_INFOS() << "LLViewerTextureList::dump()" << LL_ENDL;
 	for (image_priority_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it)
 	{
@@ -377,6 +381,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string&
 												   LLGLenum primary_format, 
 												   const LLUUID& force_id)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!mInitialized)
 	{
 		return NULL ;
@@ -404,6 +409,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string&
 												   LLGLenum primary_format, 
 												   const LLUUID& force_id)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!mInitialized)
 	{
 		return NULL ;
@@ -492,6 +498,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id,
 												   LLGLenum primary_format,
 												   LLHost request_from_host)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!mInitialized)
 	{
 		return NULL ;
@@ -554,6 +561,7 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,
 												   LLGLenum primary_format,
 												   LLHost request_from_host)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	static LLCachedControl<bool> fast_cache_fetching_enabled(gSavedSettings, "FastCacheFetchEnabled", true);
 
 	LLPointer<LLViewerFetchedTexture> imagep ;
@@ -609,6 +617,7 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,
 
 void LLViewerTextureList::findTexturesByID(const LLUUID &image_id, std::vector<LLViewerFetchedTexture*> &output)
 {
+    LL_PROFILE_ZONE_SCOPED;
     LLTextureKey search_key(image_id, TEX_LIST_STANDARD);
     uuid_map_t::iterator iter = mUUIDMap.lower_bound(search_key);
     while (iter != mUUIDMap.end() && iter->first.textureId == image_id)
@@ -634,6 +643,7 @@ LLViewerFetchedTexture *LLViewerTextureList::findImage(const LLUUID &image_id, E
 
 void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	assert_main_thread();
 	llassert_always(mInitialized) ;
 	llassert(image);
@@ -653,6 +663,7 @@ void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image)
 
 void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	assert_main_thread();
 	llassert_always(mInitialized) ;
 	llassert(image);
@@ -701,6 +712,7 @@ void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image)
 
 void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image, ETexListType tex_type)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (!new_image)
 	{
 		return;
@@ -724,6 +736,7 @@ void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image, ETexListTy
 
 void LLViewerTextureList::deleteImage(LLViewerFetchedTexture *image)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if( image)
 	{
 		if (image->hasCallbacks())
@@ -845,6 +858,7 @@ void LLViewerTextureList::updateImages(F32 max_time)
 
 void LLViewerTextureList::clearFetchingRequests()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
 	{
 		return;
@@ -862,6 +876,7 @@ void LLViewerTextureList::clearFetchingRequests()
 
 void LLViewerTextureList::updateImagesDecodePriorities()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// Update the decode priority for N images each frame
 	{
 		F32 lazy_flush_timeout = 30.f; // stop decoding
@@ -977,6 +992,7 @@ void LLViewerTextureList::updateImagesDecodePriorities()
 
 void LLViewerTextureList::setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!tex->setDebugFetching(debug_level))
 	{
 		return;
@@ -1025,6 +1041,7 @@ void LLViewerTextureList::setDebugFetching(LLViewerFetchedTexture* tex, S32 debu
 
 F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (gGLManager.mIsDisabled) return 0.0f;
 	
 	//
@@ -1041,6 +1058,7 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
 		enditer = iter;
 		LLViewerFetchedTexture *imagep = *curiter;
 		imagep->createTexture();
+        imagep->postCreateTexture();
 		if (create_timer.getElapsedTimeF32() > max_time)
 		{
 			break;
@@ -1052,6 +1070,7 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
 
 F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (gGLManager.mIsDisabled) return 0.0f;
 	if(mFastCacheList.empty())
 	{
@@ -1082,6 +1101,7 @@ F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time)
 
 void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!imagep)
 	{
 		return ;
@@ -1101,6 +1121,7 @@ void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep)
 
 F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LLTimer image_op_timer;
 	
 	// Update fetch for N images each frame
@@ -1176,6 +1197,7 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
 
 void LLViewerTextureList::updateImagesUpdateStats()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (mForceResetTextureStats)
 	{
 		for (image_priority_list_t::iterator iter = mImageList.begin();
@@ -1190,6 +1212,7 @@ void LLViewerTextureList::updateImagesUpdateStats()
 
 void LLViewerTextureList::decodeAllImages(F32 max_time)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	LLTimer timer;
 
 	//loading from fast cache 
@@ -1259,6 +1282,7 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
 										 const std::string& out_filename,
 										 const U8 codec)
 {	
+    LL_PROFILE_ZONE_SCOPED;
 	// Load the image
 	LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
 	if (image.isNull())
@@ -1312,6 +1336,7 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
 // note: modifies the argument raw_image!!!!
 LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImageRaw> raw_image)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
 	
@@ -1345,6 +1370,7 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage
 // Returns min setting for TextureMemory (in MB)
 S32Megabytes LLViewerTextureList::getMinVideoRamSetting()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	U32Megabytes system_ram = gSysMemory.getPhysicalMemoryKB();
 	//min texture mem sets to 64M if total physical mem is more than 1.5GB
 	return (system_ram > U32Megabytes(1500)) ? S32Megabytes(64) : gMinVideoRam ;
@@ -1354,6 +1380,7 @@ S32Megabytes LLViewerTextureList::getMinVideoRamSetting()
 // Returns max setting for TextureMemory (in MB)
 S32Megabytes LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended, float mem_multiplier)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	S32Megabytes max_texmem;
 	if (gGLManager.mVRAM != 0)
 	{
@@ -1407,6 +1434,7 @@ const S32Megabytes VIDEO_CARD_FRAMEBUFFER_MEM(12);
 const S32Megabytes MIN_MEM_FOR_NON_TEXTURE(512);
 void LLViewerTextureList::updateMaxResidentTexMem(S32Megabytes mem)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// Initialize the image pipeline VRAM settings
 	S32Megabytes cur_mem(gSavedSettings.getS32("TextureMemory"));
 	F32 mem_multiplier = gSavedSettings.getF32("RenderTextureMemoryMultiple");
@@ -1647,6 +1675,7 @@ void LLUIImageList::cleanUp()
 
 LLUIImagePtr LLUIImageList::getUIImageByID(const LLUUID& image_id, S32 priority)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// use id as image name
 	std::string image_name = image_id.asString();
 
@@ -1665,6 +1694,7 @@ LLUIImagePtr LLUIImageList::getUIImageByID(const LLUUID& image_id, S32 priority)
 
 LLUIImagePtr LLUIImageList::getUIImage(const std::string& image_name, S32 priority)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// look for existing image
 	uuid_ui_image_map_t::iterator found_it = mUIImages.find(image_name);
 	if (found_it != mUIImages.end())
@@ -1682,6 +1712,7 @@ LLUIImagePtr LLUIImageList::loadUIImageByName(const std::string& name, const std
 											  BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLViewerTexture::EBoostLevel boost_priority,
 											  LLUIImage::EScaleStyle scale_style)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (boost_priority == LLGLTexture::BOOST_NONE)
 	{
 		boost_priority = LLGLTexture::BOOST_UI;
@@ -1694,6 +1725,7 @@ LLUIImagePtr LLUIImageList::loadUIImageByID(const LLUUID& id,
 											BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLViewerTexture::EBoostLevel boost_priority,
 											LLUIImage::EScaleStyle scale_style)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (boost_priority == LLGLTexture::BOOST_NONE)
 	{
 		boost_priority = LLGLTexture::BOOST_UI;
@@ -1705,6 +1737,7 @@ LLUIImagePtr LLUIImageList::loadUIImageByID(const LLUUID& id,
 LLUIImagePtr LLUIImageList::loadUIImage(LLViewerFetchedTexture* imagep, const std::string& name, BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect,
 										LLUIImage::EScaleStyle scale_style)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if (!imagep) return NULL;
 
 	imagep->setAddressMode(LLTexUnit::TAM_CLAMP);
@@ -1742,6 +1775,7 @@ LLUIImagePtr LLUIImageList::loadUIImage(LLViewerFetchedTexture* imagep, const st
 
 LLUIImagePtr LLUIImageList::preloadUIImage(const std::string& name, const std::string& filename, BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLUIImage::EScaleStyle scale_style)
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// look for existing image
 	uuid_ui_image_map_t::iterator found_it = mUIImages.find(name);
 	if (found_it != mUIImages.end())
@@ -1756,6 +1790,7 @@ LLUIImagePtr LLUIImageList::preloadUIImage(const std::string& name, const std::s
 //static 
 void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* user_data )
 {
+    LL_PROFILE_ZONE_SCOPED;
 	if(!success || !user_data) 
 	{
 		return;
@@ -1857,6 +1892,7 @@ struct UIImageDeclarations : public LLInitParam::Block<UIImageDeclarations>
 
 bool LLUIImageList::initFromFile()
 {
+    LL_PROFILE_ZONE_SCOPED;
 	// Look for textures.xml in all the right places. Pass
 	// constraint=LLDir::ALL_SKINS because we want to overlay textures.xml
 	// from all the skins directories.
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index c52a4e98d3de426729334e58d6a97eb16a21a1ab..82ece85c1b5385a703ce0d9c0d85bf0fa1fa19ee 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -2011,7 +2011,7 @@ LLViewerWindow::LLViewerWindow(const Params& p)
 		
 	// Init the image list.  Must happen after GL is initialized and before the images that
 	// LLViewerWindow needs are requested.
-	LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ;
+	LLImageGL::initClass(mWindow, LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ;
 	gTextureList.init();
 	LLViewerTextureManager::init() ;
 	gBumpImageList.init();