From 967479f9039dc358b9419be7f117162f7d90609c Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Wed, 5 Oct 2011 15:12:02 -0500
Subject: [PATCH] SH-2031 Fix for stall in image update -- don't use the CPU to
 convert a height map into a normal map -- use the GPU instead via a shader. 
 Also, WTF glFinish?

---
 indra/llwindow/llwindowwin32.cpp              |   1 -
 .../shaders/class1/deferred/normgenF.glsl     |  56 ++++++
 .../shaders/class1/deferred/normgenV.glsl     |  36 ++++
 indra/newview/lldrawpoolbump.cpp              | 176 +++++++++++++-----
 indra/newview/llviewerdisplay.cpp             |  35 +++-
 indra/newview/llviewershadermgr.cpp           |  14 ++
 indra/newview/llviewershadermgr.h             |   2 +-
 indra/newview/llviewertexturelist.cpp         |  66 ++++---
 8 files changed, 305 insertions(+), 81 deletions(-)
 create mode 100644 indra/newview/app_settings/shaders/class1/deferred/normgenF.glsl
 create mode 100644 indra/newview/app_settings/shaders/class1/deferred/normgenV.glsl

diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index e4e5256ae70..bac23279cc1 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -2925,7 +2925,6 @@ BOOL LLWindowWin32::resetDisplayResolution()
 
 void LLWindowWin32::swapBuffers()
 {
-	glFinish();
 	SwapBuffers(mhDC);
 }
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/normgenF.glsl b/indra/newview/app_settings/shaders/class1/deferred/normgenF.glsl
new file mode 100644
index 00000000000..879942d8faa
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/normgenF.glsl
@@ -0,0 +1,56 @@
+/** 
+ * @file normgenF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2007, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+ 
+#extension GL_ARB_texture_rectangle : enable
+
+#ifdef DEFINE_GL_FRAGCOLOR
+out vec4 gl_FragColor;
+#endif
+
+uniform sampler2D alphaMap;
+
+VARYING vec2 vary_texcoord0;
+
+uniform float stepX;
+uniform float stepY;
+uniform float norm_scale;
+
+void main()
+{
+	float alpha = texture2D(alphaMap, vary_texcoord0).a;
+
+	vec3 right = vec3(norm_scale, 0, (texture2D(alphaMap, vary_texcoord0+vec2(stepX, 0)).a-alpha)*255);
+	vec3 left = vec3(-norm_scale, 0, (texture2D(alphaMap, vary_texcoord0-vec2(stepX, 0)).a-alpha)*255);
+	vec3 up = vec3(0, -norm_scale, (texture2D(alphaMap, vary_texcoord0-vec2(0, stepY)).a-alpha)*255);
+	vec3 down = vec3(0, norm_scale, (texture2D(alphaMap, vary_texcoord0+vec2(0, stepY)).a-alpha)*255);
+	
+	vec3 norm = cross(right, down) + cross(down, left) + cross(left,up) + cross(up, right);
+	
+	norm = normalize(norm);
+	norm *= 0.5;
+	norm += 0.5;	
+	
+	gl_FragColor = vec4(norm, alpha);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/normgenV.glsl b/indra/newview/app_settings/shaders/class1/deferred/normgenV.glsl
new file mode 100644
index 00000000000..9bceae05b71
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/normgenV.glsl
@@ -0,0 +1,36 @@
+/** 
+ * @file normgenV.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2007, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+ 
+ATTRIBUTE vec3 position;
+ATTRIBUTE vec2 texcoord0;
+
+VARYING vec2 vary_fragcoord;
+VARYING vec2 vary_texcoord0;
+
+void main()
+{
+	gl_Position = vec4(position.x*2.0-1.0, position.y*2.0-1.0, -1.0, 1.0); 
+	vary_texcoord0 = texcoord0;
+}
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index 501bd889704..be12eb800f1 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -1088,6 +1088,8 @@ LLViewerTexture* LLBumpImageList::getBrightnessDarknessImage(LLViewerFetchedText
 }
 
 
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_STANDARD_LOADED("Bump Standard Callback");
+
 // static
 void LLBumpImageList::onSourceBrightnessLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
 {
@@ -1110,14 +1112,24 @@ void LLBumpImageList::onSourceDarknessLoaded( BOOL success, LLViewerFetchedTextu
 	}
 }
 
+static LLFastTimer::DeclareTimer FTM_BUMP_GEN_NORMAL("Generate Normal Map");
+static LLFastTimer::DeclareTimer FTM_BUMP_CREATE_TEXTURE("Create GL Normal Map");
+
 void LLBumpImageList::onSourceStandardLoaded( BOOL success, LLViewerFetchedTexture* src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata)
 {
 	if (success && LLPipeline::sRenderDeferred)
 	{
+		LLFastTimer t(FTM_BUMP_SOURCE_STANDARD_LOADED);
 		LLPointer<LLImageRaw> nrm_image = new LLImageRaw(src->getWidth(), src->getHeight(), 4);
-		generateNormalMapFromAlpha(src, nrm_image);
+		{
+			LLFastTimer t(FTM_BUMP_GEN_NORMAL);
+			generateNormalMapFromAlpha(src, nrm_image);
+		}
 		src_vi->setExplicitFormat(GL_RGBA, GL_RGBA);
-		src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image);
+		{
+			LLFastTimer t(FTM_BUMP_CREATE_TEXTURE);
+			src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image);
+		}
 	}
 }
 
@@ -1176,24 +1188,39 @@ void LLBumpImageList::generateNormalMapFromAlpha(LLImageRaw* src, LLImageRaw* nr
 	}
 }
 
+
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_LOADED("Bump Source Loaded");
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_ENTRIES_UPDATE("Entries Update");
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_MIN_MAX("Min/Max");
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_RGB2LUM("RGB to Luminance");
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_RESCALE("Rescale");
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_GEN_NORMAL("Generate Normal");
+static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_CREATE("Create");
+
 // static
 void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump_code )
 {
 	if( success )
 	{
+		LLFastTimer t(FTM_BUMP_SOURCE_LOADED);
+
+
 		bump_image_map_t& entries_list(bump_code == BE_BRIGHTNESS ? gBumpImageList.mBrightnessEntries : gBumpImageList.mDarknessEntries );
 		bump_image_map_t::iterator iter = entries_list.find(source_asset_id);
 
-		if (iter == entries_list.end() ||
-			iter->second.isNull() ||
-                        iter->second->getWidth() != src->getWidth() ||
-                        iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution
-		{ //make sure an entry exists for this image
-			LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
-			raw->clear(0x77, 0x77, 0xFF, 0xFF);
-
-			entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE);
-			iter = entries_list.find(src_vi->getID());
+		{
+			LLFastTimer t(FTM_BUMP_SOURCE_ENTRIES_UPDATE);
+			if (iter == entries_list.end() ||
+				iter->second.isNull() ||
+							iter->second->getWidth() != src->getWidth() ||
+							iter->second->getHeight() != src->getHeight()) // bump not cached yet or has changed resolution
+			{ //make sure an entry exists for this image
+				LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
+				raw->clear(0x77, 0x77, 0xFF, 0xFF);
+
+				entries_list[src_vi->getID()] = LLViewerTextureManager::getLocalTexture( raw.get(), TRUE);
+				iter = entries_list.find(src_vi->getID());
+			}
 		}
 
 		//if (iter->second->getWidth() != src->getWidth() ||
@@ -1224,50 +1251,56 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI
 			{
 			case 1:
 			case 2:
-				if( src_data_size == dst_data_size * src_components )
 				{
-					for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+					LLFastTimer t(FTM_BUMP_SOURCE_MIN_MAX);
+					if( src_data_size == dst_data_size * src_components )
 					{
-						dst_data[i] = src_data[j];
-						if( dst_data[i] < minimum )
+						for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
 						{
-							minimum = dst_data[i];
-						}
-						if( dst_data[i] > maximum )
-						{
-							maximum = dst_data[i];
+							dst_data[i] = src_data[j];
+							if( dst_data[i] < minimum )
+							{
+								minimum = dst_data[i];
+							}
+							if( dst_data[i] > maximum )
+							{
+								maximum = dst_data[i];
+							}
 						}
 					}
-				}
-				else
-				{
-					llassert(0);
-					dst_image->clear();
+					else
+					{
+						llassert(0);
+						dst_image->clear();
+					}
 				}
 				break;
 			case 3:
 			case 4:
-				if( src_data_size == dst_data_size * src_components )
 				{
-					for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+					LLFastTimer t(FTM_BUMP_SOURCE_RGB2LUM);
+					if( src_data_size == dst_data_size * src_components )
 					{
-						// RGB to luminance
-						dst_data[i] = (R_WEIGHT * src_data[j] + G_WEIGHT * src_data[j+1] + B_WEIGHT * src_data[j+2]) >> FIXED_PT;
-						//llassert( dst_data[i] <= 255 );true because it's 8bit
-						if( dst_data[i] < minimum )
-						{
-							minimum = dst_data[i];
-						}
-						if( dst_data[i] > maximum )
+						for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
 						{
-							maximum = dst_data[i];
+							// RGB to luminance
+							dst_data[i] = (R_WEIGHT * src_data[j] + G_WEIGHT * src_data[j+1] + B_WEIGHT * src_data[j+2]) >> FIXED_PT;
+							//llassert( dst_data[i] <= 255 );true because it's 8bit
+							if( dst_data[i] < minimum )
+							{
+								minimum = dst_data[i];
+							}
+							if( dst_data[i] > maximum )
+							{
+								maximum = dst_data[i];
+							}
 						}
 					}
-				}
-				else
-				{
-					llassert(0);
-					dst_image->clear();
+					else
+					{
+						llassert(0);
+						dst_image->clear();
+					}
 				}
 				break;
 			default:
@@ -1278,6 +1311,7 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI
 
 			if( maximum > minimum )
 			{
+				LLFastTimer t(FTM_BUMP_SOURCE_RESCALE);
 				U8 bias_and_scale_lut[256];
 				F32 twice_one_over_range = 2.f / (maximum - minimum);
 				S32 i;
@@ -1311,17 +1345,63 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI
 			// accidentally releases it.
 			LLPointer<LLViewerTexture> bump = LLViewerTextureManager::getLocalTexture( TRUE );
 			
+			
 			if (!LLPipeline::sRenderDeferred)
 			{
+				LLFastTimer t(FTM_BUMP_SOURCE_CREATE);
 				bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
 				bump->createGLTexture(0, dst_image);
 			}
-			else
-			{
-				LLPointer<LLImageRaw> nrm_image = new LLImageRaw(dst_image->getWidth(), dst_image->getHeight(), 4);
-				generateNormalMapFromAlpha(dst_image, nrm_image);
-				bump->setExplicitFormat(GL_RGBA, GL_RGBA);
-				bump->createGLTexture(0, nrm_image);
+			else 
+			{ //convert to normal map
+				{
+					LLFastTimer t(FTM_BUMP_SOURCE_CREATE);
+					bump->setExplicitFormat(GL_RGBA8, GL_ALPHA);
+					bump->createGLTexture(0, dst_image);
+				}
+
+				{
+					LLFastTimer t(FTM_BUMP_SOURCE_GEN_NORMAL);
+					gPipeline.mScreen.bindTarget();
+					LLGLDepthTest depth(GL_FALSE);
+					LLGLDisable cull(GL_CULL_FACE);
+					LLGLDisable blend(GL_BLEND);
+					gGL.setColorMask(TRUE, TRUE);
+					gNormalMapGenProgram.bind();
+					gNormalMapGenProgram.uniform1f("norm_scale", gSavedSettings.getF32("RenderNormalMapScale"));
+					gNormalMapGenProgram.uniform1f("stepX", 1.f/bump->getWidth());
+					gNormalMapGenProgram.uniform1f("stepY", 1.f/bump->getHeight());
+
+					LLVector2 v((F32) bump->getWidth()/gPipeline.mScreen.getWidth(),
+								(F32) bump->getHeight()/gPipeline.mScreen.getHeight());
+
+					gGL.getTexUnit(0)->bind(bump);
+					
+					gGL.begin(LLRender::TRIANGLE_STRIP);
+
+					gGL.texCoord2f(0, 0);
+					gGL.vertex2f(0, 0);
+					gGL.texCoord2f(0, 1);
+					gGL.vertex2f(0, v.mV[1]);
+					gGL.texCoord2f(1, 0);
+					gGL.vertex2f(v.mV[0], 0);
+					gGL.texCoord2f(1, 1);
+					gGL.vertex2f(v.mV[0], v.mV[1]);
+
+					gGL.end();
+
+					gGL.flush();
+
+					glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, bump->getWidth(), bump->getHeight());
+
+					glGenerateMipmap(GL_TEXTURE_2D);
+
+					gPipeline.mScreen.flush();
+
+					gNormalMapGenProgram.unbind();
+										
+					//generateNormalMapFromAlpha(dst_image, nrm_image);
+				}
 			}
 		
 			iter->second = bump; // derefs (and deletes) old image
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 20c90710529..85ef12b32bd 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -213,6 +213,10 @@ static LLFastTimer::DeclareTimer FTM_RENDER("Render", true);
 static LLFastTimer::DeclareTimer FTM_UPDATE_SKY("Update Sky");
 static LLFastTimer::DeclareTimer FTM_UPDATE_TEXTURES("Update Textures");
 static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE("Update Images");
+static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_CLASS("Class");
+static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_BUMP("Bump");
+static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_LIST("List");
+static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_DELETE("Delete");
 
 // Paint the display!
 void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
@@ -743,18 +747,31 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 			LLMemType mt_iu(LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE);
 			LLFastTimer t(FTM_IMAGE_UPDATE);
 			
-			LLViewerTexture::updateClass(LLViewerCamera::getInstance()->getVelocityStat()->getMean(),
-										LLViewerCamera::getInstance()->getAngularVelocityStat()->getMean());
+			{
+				LLFastTimer t(FTM_IMAGE_UPDATE_CLASS);
+				LLViewerTexture::updateClass(LLViewerCamera::getInstance()->getVelocityStat()->getMean(),
+											LLViewerCamera::getInstance()->getAngularVelocityStat()->getMean());
+			}
 
-			gBumpImageList.updateImages();  // must be called before gTextureList version so that it's textures are thrown out first.
+			
+			{
+				LLFastTimer t(FTM_IMAGE_UPDATE_BUMP);
+				gBumpImageList.updateImages();  // must be called before gTextureList version so that it's textures are thrown out first.
+			}
 
-			F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds; // 50 ms/second decode time
-			max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f ); // min 2ms/frame, max 5ms/frame)
-			gTextureList.updateImages(max_image_decode_time);
+			{
+				LLFastTimer t(FTM_IMAGE_UPDATE_LIST);
+				F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds; // 50 ms/second decode time
+				max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f ); // min 2ms/frame, max 5ms/frame)
+				gTextureList.updateImages(max_image_decode_time);
+			}
 
-			//remove dead textures from GL
-			LLImageGL::deleteDeadTextures();
-			stop_glerror();
+			{
+				LLFastTimer t(FTM_IMAGE_UPDATE_DELETE);
+				//remove dead textures from GL
+				LLImageGL::deleteDeadTextures();
+				stop_glerror();
+			}
 		}
 
 		LLGLState::checkStates();
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 764c247f012..ac489e0caf5 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -188,6 +188,7 @@ LLGLSLShader			gDeferredPostNoDoFProgram;
 LLGLSLShader			gDeferredWLSkyProgram;
 LLGLSLShader			gDeferredWLCloudProgram;
 LLGLSLShader			gDeferredStarProgram;
+LLGLSLShader			gNormalMapGenProgram;
 
 LLViewerShaderMgr::LLViewerShaderMgr() :
 	mVertexShaderLevel(SHADER_COUNT, 0),
@@ -275,6 +276,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
 	mShaderList.push_back(&gDeferredWLSkyProgram);
 	mShaderList.push_back(&gDeferredWLCloudProgram);
 	mShaderList.push_back(&gDeferredStarProgram);
+	mShaderList.push_back(&gNormalMapGenProgram);
 }
 
 LLViewerShaderMgr::~LLViewerShaderMgr()
@@ -1082,6 +1084,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredWLSkyProgram.unload();
 		gDeferredWLCloudProgram.unload();
 		gDeferredStarProgram.unload();
+		gNormalMapGenProgram.unload();
 		return TRUE;
 	}
 
@@ -1532,6 +1535,17 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		success = gDeferredStarProgram.createShader(NULL, &mWLUniforms);
 	}
 
+	if (success)
+	{
+		gNormalMapGenProgram.mName = "Normal Map Generation Program";
+		gNormalMapGenProgram.mShaderFiles.clear();
+		gNormalMapGenProgram.mShaderFiles.push_back(make_pair("deferred/normgenV.glsl", GL_VERTEX_SHADER_ARB));
+		gNormalMapGenProgram.mShaderFiles.push_back(make_pair("deferred/normgenF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gNormalMapGenProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+		gNormalMapGenProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+		success = gNormalMapGenProgram.createShader(NULL, NULL);
+	}
+
 	return success;
 }
 
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 74708c94a7c..5bcdf11be5b 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -384,6 +384,6 @@ extern LLGLSLShader			gDeferredAvatarAlphaProgram;
 extern LLGLSLShader			gDeferredWLSkyProgram;
 extern LLGLSLShader			gDeferredWLCloudProgram;
 extern LLGLSLShader			gDeferredStarProgram;
-
+extern LLGLSLShader			gNormalMapGenProgram;
 
 #endif
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 30ef8b8a292..487cdafd8f1 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -586,6 +586,11 @@ void LLViewerTextureList::dirtyImage(LLViewerFetchedTexture *image)
 
 ////////////////////////////////////////////////////////////////////////////
 static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images");
+static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_PRIORITIES("Prioritize");
+static LLFastTimer::DeclareTimer FTM_IMAGE_CALLBACKS("Callbacks");
+static LLFastTimer::DeclareTimer FTM_IMAGE_FETCH("Fetch");
+static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create");
+static LLFastTimer::DeclareTimer FTM_IMAGE_STATS("Stats");
 
 void LLViewerTextureList::updateImages(F32 max_time)
 {
@@ -597,14 +602,25 @@ void LLViewerTextureList::updateImages(F32 max_time)
 	LLViewerStats::getInstance()->mGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes));
 	LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageRaw::sGlobalRawMemory));
 	LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory));
-	
-	updateImagesDecodePriorities();
+
+
+	{
+		LLFastTimer t(FTM_IMAGE_UPDATE_PRIORITIES);
+		updateImagesDecodePriorities();
+	}
 
 	F32 total_max_time = max_time;
-	max_time -= updateImagesFetchTextures(max_time);
+
+	{
+		LLFastTimer t(FTM_IMAGE_FETCH);
+		max_time -= updateImagesFetchTextures(max_time);
+	}
 	
-	max_time = llmax(max_time, total_max_time*.50f); // at least 50% of max_time
-	max_time -= updateImagesCreateTextures(max_time);
+	{
+		LLFastTimer t(FTM_IMAGE_CREATE);
+		max_time = llmax(max_time, total_max_time*.50f); // at least 50% of max_time
+		max_time -= updateImagesCreateTextures(max_time);
+	}
 	
 	if (!mDirtyTextureList.empty())
 	{
@@ -612,24 +628,32 @@ void LLViewerTextureList::updateImages(F32 max_time)
 		gPipeline.dirtyPoolObjectTextures(mDirtyTextureList);
 		mDirtyTextureList.clear();
 	}
-	bool didone = false;
-	for (image_list_t::iterator iter = mCallbackList.begin();
-		iter != mCallbackList.end(); )
+
 	{
-		//trigger loaded callbacks on local textures immediately
-		LLViewerFetchedTexture* image = *iter++;
-		if (!image->getUrl().empty())
+		LLFastTimer t(FTM_IMAGE_CALLBACKS);
+		bool didone = false;
+		for (image_list_t::iterator iter = mCallbackList.begin();
+			iter != mCallbackList.end(); )
 		{
-			// Do stuff to handle callbacks, update priorities, etc.
-			didone = image->doLoadedCallbacks();
-		}
-		else if (!didone)
-		{
-			// Do stuff to handle callbacks, update priorities, etc.
-			didone = image->doLoadedCallbacks();
+			//trigger loaded callbacks on local textures immediately
+			LLViewerFetchedTexture* image = *iter++;
+			if (!image->getUrl().empty())
+			{
+				// Do stuff to handle callbacks, update priorities, etc.
+				didone = image->doLoadedCallbacks();
+			}
+			else if (!didone)
+			{
+				// Do stuff to handle callbacks, update priorities, etc.
+				didone = image->doLoadedCallbacks();
+			}
 		}
 	}
-	updateImagesUpdateStats();
+
+	{
+		LLFastTimer t(FTM_IMAGE_STATS);
+		updateImagesUpdateStats();
+	}
 }
 
 void LLViewerTextureList::updateImagesDecodePriorities()
@@ -747,7 +771,6 @@ void LLViewerTextureList::updateImagesDecodePriorities()
  return type_from_host;
  }
  */
-static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create Images");
 
 F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
 {
@@ -757,8 +780,7 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
 	// Create GL textures for all textures that need them (images which have been
 	// decoded, but haven't been pushed into GL).
 	//
-	LLFastTimer t(FTM_IMAGE_CREATE);
-	
+		
 	LLTimer create_timer;
 	image_list_t::iterator enditer = mCreateTextureList.begin();
 	for (image_list_t::iterator iter = mCreateTextureList.begin();
-- 
GitLab