From 5ab4dadc703a0f7419929c7da57c471f2ece8a6c Mon Sep 17 00:00:00 2001
From: Xiaohong Bao <bao@lindenlab.com>
Date: Wed, 25 Apr 2012 16:59:20 -0600
Subject: [PATCH] for subtasks SH-3118, SH-3112, SH-3110, SH-3106, SH-3091 for
 SH-3086: As a viewer architect, I would like to understand how fast each of
 the components of the texture pipeline can run in isolation

---
 indra/llmessage/llcurl.cpp                    |   2 +-
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/llappviewer.cpp                 |   9 +
 .../newview/llfloatertexturefetchdebugger.cpp | 330 +++++++++++++
 indra/newview/llfloatertexturefetchdebugger.h |  70 +++
 indra/newview/lltexturefetch.cpp              | 436 +++++++++++++++++-
 indra/newview/lltexturefetch.h                | 148 +++++-
 indra/newview/llviewerfloaterreg.cpp          |   2 +
 indra/newview/llviewertexture.cpp             |   7 +-
 indra/newview/llviewertexture.h               |   1 +
 indra/newview/llviewertexturelist.h           |   6 +-
 .../xui/en/floater_texture_fetch_debugger.xml | 292 ++++++++++++
 .../skins/default/xui/en/menu_viewer.xml      |   7 +
 13 files changed, 1305 insertions(+), 7 deletions(-)
 create mode 100644 indra/newview/llfloatertexturefetchdebugger.cpp
 create mode 100644 indra/newview/llfloatertexturefetchdebugger.h
 create mode 100644 indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml

diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index 3bcaffc2758..f58a4f5caf4 100644
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -1506,7 +1506,7 @@ void LLCurl::cleanupClass()
 	delete sHandleMutexp ;
 	sHandleMutexp = NULL ;
 
-	llassert(Easy::sActiveHandles.empty());
+	//llassert(Easy::sActiveHandles.empty());
 }
 
 //static 
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index f85b943c70d..6db97390aac 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -236,6 +236,7 @@ set(viewer_SOURCE_FILES
     llfloatertelehub.cpp
     llfloatertestinspectors.cpp
     llfloatertestlistview.cpp
+	llfloatertexturefetchdebugger.cpp
     llfloatertools.cpp
     llfloatertopobjects.cpp
     llfloatertos.cpp
@@ -792,6 +793,7 @@ set(viewer_HEADER_FILES
     llfloatertelehub.h
     llfloatertestinspectors.h
     llfloatertestlistview.h
+	llfloatertexturefetchdebugger.h
     llfloatertools.h
     llfloatertopobjects.h
     llfloatertos.h
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index a627f3868b8..3ee53c679f3 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -93,6 +93,7 @@
 #include "llsecondlifeurls.h"
 #include "llupdaterservice.h"
 #include "llcallfloater.h"
+#include "llfloatertexturefetchdebugger.h"
 
 // Linden library includes
 #include "llavatarnamecache.h"
@@ -1220,6 +1221,14 @@ bool LLAppViewer::mainLoop()
 				mem_leak_instance->idle() ;				
 			}			
 
+			//texture fetching debugger
+			LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
+				LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
+			if(tex_fetch_debugger_instance)
+			{
+				tex_fetch_debugger_instance->idle() ;				
+			}			
+
             // canonical per-frame event
             mainloop.post(newFrame);
 
diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp
new file mode 100644
index 00000000000..6756f9bbc18
--- /dev/null
+++ b/indra/newview/llfloatertexturefetchdebugger.cpp
@@ -0,0 +1,330 @@
+/** 
+ * @file llfloatertexturefetchdebugger.cpp
+ * @brief LLFloaterTextureFetchDebugger class definition
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertexturefetchdebugger.h"
+
+#include "lluictrlfactory.h"
+#include "llbutton.h"
+#include "llspinctrl.h"
+#include "llresmgr.h"
+
+#include "llmath.h"
+#include "llviewerwindow.h"
+#include "llappviewer.h"
+#include "lltexturefetch.h"
+
+//static
+F32 LLFloaterTextureFetchDebugger::sTexelPixelRatio = 1.0f;
+
+LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key)
+	: LLFloater(key),
+	mDebugger(NULL)
+{
+	setTitle("Texture Fetching Debugger Floater");
+	
+	mCommitCallbackRegistrar.add("TexFetchDebugger.ChangeTexelPixelRatio",	boost::bind(&LLFloaterTextureFetchDebugger::onChangeTexelPixelRatio, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Start",	boost::bind(&LLFloaterTextureFetchDebugger::onClickStart, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Clear",	boost::bind(&LLFloaterTextureFetchDebugger::onClickClear, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Close",	boost::bind(&LLFloaterTextureFetchDebugger::onClickClose, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.CacheRead",	boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheRead, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.CacheWrite",	boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheWrite, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.HTTPLoad",	boost::bind(&LLFloaterTextureFetchDebugger::onClickHTTPLoad, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Decode",	boost::bind(&LLFloaterTextureFetchDebugger::onClickDecode, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.GLTexture",	boost::bind(&LLFloaterTextureFetchDebugger::onClickGLTexture, this));
+}
+//----------------------------------------------
+
+BOOL LLFloaterTextureFetchDebugger::postBuild(void) 
+{	
+	mDebugger = LLAppViewer::getTextureFetch()->getFetchDebugger();
+
+	//set states for buttons
+	mButtonStateMap["start_btn"] = true;
+	mButtonStateMap["close_btn"] = true;
+	mButtonStateMap["clear_btn"] = true;
+	mButtonStateMap["cacheread_btn"] = false;
+	mButtonStateMap["cachewrite_btn"] = false;
+	mButtonStateMap["http_btn"] = false;
+	mButtonStateMap["decode_btn"] = false;
+	mButtonStateMap["gl_btn"] = false;
+	updateButtons();
+
+	return TRUE ;
+}
+
+LLFloaterTextureFetchDebugger::~LLFloaterTextureFetchDebugger()
+{
+	//stop everything
+	mDebugger->stopDebug();
+}
+
+void LLFloaterTextureFetchDebugger::updateButtons()
+{
+	for(std::map<std::string, bool>::iterator iter = mButtonStateMap.begin(); iter != mButtonStateMap.end(); ++iter)
+	{
+		if(iter->second)
+		{
+			childEnable(iter->first.c_str());
+		}
+		else
+		{
+			childDisable(iter->first.c_str());
+		}
+	}
+}
+
+void LLFloaterTextureFetchDebugger::disableButtons()
+{
+	childDisable("start_btn");
+	childDisable("clear_btn");
+	childDisable("cacheread_btn");
+	childDisable("cachewrite_btn");
+	childDisable("http_btn");
+	childDisable("decode_btn");
+	childDisable("gl_btn");
+}
+
+void LLFloaterTextureFetchDebugger::idle()
+{	
+	LLTextureFetchDebugger::e_debug_state state = mDebugger->getState();
+	
+	if(mDebugger->update())
+	{
+		switch(state)
+		{
+		case LLTextureFetchDebugger::IDLE:
+			break;
+		case LLTextureFetchDebugger::READ_CACHE:
+			mButtonStateMap["cachewrite_btn"] = true;
+			mButtonStateMap["decode_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::WRITE_CACHE:
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::DECODING:
+			mButtonStateMap["gl_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::HTTP_FETCHING:
+			mButtonStateMap["cacheread_btn"] = true;
+			mButtonStateMap["cachewrite_btn"] = true;
+			mButtonStateMap["decode_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::GL_TEX:
+			updateButtons();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+//----------------------
+void LLFloaterTextureFetchDebugger::onChangeTexelPixelRatio()
+{
+	sTexelPixelRatio = getChild<LLUICtrl>("texel_pixel_ratio")->getValue().asReal();
+}
+
+void LLFloaterTextureFetchDebugger::onClickStart()
+{
+	disableButtons();
+
+	mDebugger->startDebug();
+
+	mButtonStateMap["start_btn"] = false;
+	mButtonStateMap["cacheread_btn"] = true;
+	mButtonStateMap["http_btn"] = true;
+	updateButtons();
+}
+
+void LLFloaterTextureFetchDebugger::onClickClose()
+{
+	setVisible(FALSE);
+	
+	//stop everything
+	mDebugger->stopDebug();
+}
+
+void LLFloaterTextureFetchDebugger::onClickClear()
+{
+	mButtonStateMap["start_btn"] = true;
+	mButtonStateMap["close_btn"] = true;
+	mButtonStateMap["clear_btn"] = true;
+	mButtonStateMap["cacheread_btn"] = false;
+	mButtonStateMap["cachewrite_btn"] = false;
+	mButtonStateMap["http_btn"] = false;
+	mButtonStateMap["decode_btn"] = false;
+	mButtonStateMap["gl_btn"] = false;
+	updateButtons();
+
+	//stop everything
+	mDebugger->stopDebug();
+	mDebugger->clearHistory();
+}
+
+void LLFloaterTextureFetchDebugger::onClickCacheRead()
+{
+	disableButtons();
+
+	mDebugger->debugCacheRead();
+}
+
+void LLFloaterTextureFetchDebugger::onClickCacheWrite()
+{
+	disableButtons();
+
+	mDebugger->debugCacheWrite();
+}
+
+void LLFloaterTextureFetchDebugger::onClickHTTPLoad()
+{
+	disableButtons();
+
+	mDebugger->debugHTTP();
+}
+
+void LLFloaterTextureFetchDebugger::onClickDecode()
+{
+	disableButtons();
+
+	mDebugger->debugDecoder();
+}
+
+void LLFloaterTextureFetchDebugger::onClickGLTexture()
+{
+	disableButtons();
+
+	mDebugger->debugGLTextureCreation();
+}
+
+void LLFloaterTextureFetchDebugger::draw()
+{
+	//total number of fetched textures
+	{
+		getChild<LLUICtrl>("total_num_fetched_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumFetchedTextures()));
+	}
+
+	//total number of fetching requests
+	{
+		getChild<LLUICtrl>("total_num_fetching_requests_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumFetchingRequests()));
+	}
+
+	//total number of cache hits
+	{
+		getChild<LLUICtrl>("total_num_cache_hits_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumCacheHits()));
+	}
+
+	//total number of visible textures
+	{
+		getChild<LLUICtrl>("total_num_visible_tex_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumVisibleFetchedTextures()));
+	}
+
+	//total number of visible texture fetching requests
+	{
+		getChild<LLUICtrl>("total_num_visible_tex_fetch_req_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumVisibleFetchingRequests()));
+	}
+
+	//total number of fetched data
+	{
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getFetchedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getDecodedData() >> 10));
+	}
+
+	//total number of visible fetched data
+	{		
+		getChild<LLUICtrl>("total_fetched_vis_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getVisibleFetchedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_vis_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getVisibleDecodedData() >> 10));
+	}
+
+	//total number of rendered fetched data
+	{
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getRenderedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getRenderedDecodedData() >> 10));
+	}
+
+	//total time on cache readings
+	if(mDebugger->getCacheReadTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_cache_read_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_cache_read_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getCacheReadTime()));
+	}
+
+	//total time on cache writings
+	if(mDebugger->getCacheWriteTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_cache_write_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_cache_write_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getCacheWriteTime()));
+	}
+
+	//total time on decoding
+	if(mDebugger->getDecodeTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_decode_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_decode_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getDecodeTime()));
+	}
+
+	//total time on gl texture creation
+	if(mDebugger->getGLCreationTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_gl_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_gl_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getGLCreationTime()));
+	}
+
+	//total time on HTTP fetching
+	if(mDebugger->getHTTPTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_http_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getHTTPTime()));
+	}
+
+	//total time on entire fetching
+	{
+		getChild<LLUICtrl>("total_time_fetch_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getTotalFetchingTime()));
+	}
+
+	LLFloater::draw();
+}
diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h
new file mode 100644
index 00000000000..9f2e62fd2cc
--- /dev/null
+++ b/indra/newview/llfloatertexturefetchdebugger.h
@@ -0,0 +1,70 @@
+/** 
+ * @file llfloatertexturefetchdebugger.h
+ * @brief texture fetching debugger window, debug use only
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+
+#ifndef LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
+#define LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
+
+#include "llfloater.h"
+class LLTextureFetchDebugger;
+
+class LLFloaterTextureFetchDebugger : public LLFloater
+{
+	friend class LLFloaterReg;
+public:
+	/// initialize all the callbacks for the menu
+
+	virtual BOOL postBuild() ;
+	virtual void draw() ;
+	
+	void onChangeTexelPixelRatio();
+	
+	void onClickStart();
+	void onClickClear();
+	void onClickClose();
+
+	void onClickCacheRead();
+	void onClickCacheWrite();
+	void onClickHTTPLoad();
+	void onClickDecode();
+	void onClickGLTexture();
+
+public:
+	void idle() ;
+
+private:	
+	LLFloaterTextureFetchDebugger(const LLSD& key);
+	virtual ~LLFloaterTextureFetchDebugger();
+
+	void updateButtons();
+	void disableButtons();
+private:
+	static F32 sTexelPixelRatio;
+
+	LLTextureFetchDebugger* mDebugger;
+	std::map<std::string, bool> mButtonStateMap;
+};
+
+#endif // LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 2e1b409fa7c..d7742219bfb 100755
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -54,6 +54,7 @@
 #include "llworld.h"
 #include "llsdutil.h"
 #include "llstartup.h"
+#include "llviewerstats.h"
 
 LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128);
 LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128);
@@ -63,6 +64,7 @@ class LLTextureFetchWorker : public LLWorkerClass
 {
 	friend class LLTextureFetch;
 	friend class HTTPGetResponder;
+	friend class LLTextureFetchDebugger;
 	
 private:
 	class CacheReadResponder : public LLTextureCache::ReadResponder
@@ -265,6 +267,7 @@ class LLTextureFetchWorker : public LLWorkerClass
 	BOOL mNeedsAux;
 	BOOL mHaveAllData;
 	BOOL mInLocalCache;
+	BOOL mInCache;
 	bool mCanUseHTTP ;
 	bool mCanUseNET ; //can get from asset server.
 	S32 mHTTPFailCount;
@@ -677,6 +680,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 	  mNeedsAux(FALSE),
 	  mHaveAllData(FALSE),
 	  mInLocalCache(FALSE),
+	  mInCache(FALSE),
 	  mCanUseHTTP(true),
 	  mHTTPFailCount(0),
 	  mRetryAttempt(0),
@@ -906,6 +910,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 		mCacheReadHandle = LLTextureCache::nullHandle();
 		mCacheWriteHandle = LLTextureCache::nullHandle();
 		mState = LOAD_FROM_TEXTURE_CACHE;
+		mInCache = FALSE;
 		mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
 		LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
 							 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
@@ -994,6 +999,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			llassert_always(mFormattedImage->getDataSize() > 0);
 			mLoadedDiscard = mDesiredDiscard;
 			mState = DECODE_IMAGE;
+			mInCache = TRUE;
 			mWriteToCacheState = NOT_WRITE ;
 			LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
 								 << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
@@ -1418,6 +1424,11 @@ bool LLTextureFetchWorker::doWork(S32 param)
 	{
 		if (mDecoded)
 		{
+			if(!mInLocalCache)
+			{
+				mFetcher->getFetchDebugger()->addHistoryEntry(this);
+			}
+
 			if (mDecodedDiscard < 0)
 			{
 				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
@@ -1852,6 +1863,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	mCurlPOSTRequestCount = 0;
 	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
 	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
+
+	mFetchDebugger = new LLTextureFetchDebugger(this, cache, imagedecodethread) ;
 }
 
 LLTextureFetch::~LLTextureFetch()
@@ -1866,11 +1879,17 @@ LLTextureFetch::~LLTextureFetch()
 	}
 	
 	// ~LLQueuedThread() called here
+
+	delete mFetchDebugger;
 }
 
 bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
 								   S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
 {
+	if(mFetcherLocked)
+	{
+		return false;
+	}
 	if (mDebugPause)
 	{
 		return false;
@@ -2292,6 +2311,7 @@ void LLTextureFetch::startThread()
 {
 	// Construct mCurlGetRequest from Worker Thread
 	mCurlGetRequest = new LLCurlRequest();
+	mFetchDebugger->setCurlGetRequest(mCurlGetRequest);
 }
 
 // WORKER THREAD
@@ -2300,6 +2320,7 @@ void LLTextureFetch::endThread()
 	// Destroy mCurlGetRequest from Worker Thread
 	delete mCurlGetRequest;
 	mCurlGetRequest = NULL;
+	mFetchDebugger->setCurlGetRequest(NULL);
 }
 
 // WORKER THREAD
@@ -2837,7 +2858,6 @@ void LLTextureFetch::cmdDoWork()
 	}
 }
 
-
 //////////////////////////////////////////////////////////////////////////////
 
 // Private (anonymous) class methods implementing the command scheme.
@@ -3041,5 +3061,419 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics)
 
 } // end of anonymous namespace
 
+///////////////////////////////////////////////////////////////////////////////////////////
+//Start LLTextureFetchDebugger
+///////////////////////////////////////////////////////////////////////////////////////////
+//---------------------
+class LLDebuggerCacheReadResponder : public LLTextureCache::ReadResponder
+{
+public:
+	LLDebuggerCacheReadResponder(LLTextureFetchDebugger* debugger, S32 id, LLImageFormatted* image)
+		: mDebugger(debugger), mID(id)
+	{
+		setImage(image);
+	}
+	virtual void completed(bool success)
+	{
+		mDebugger->callbackCacheRead(mID, success, mFormattedImage, mImageSize, mImageLocal);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerCacheWriteResponder : public LLTextureCache::WriteResponder
+{
+public:
+	LLDebuggerCacheWriteResponder(LLTextureFetchDebugger* debugger, S32 id)
+		: mDebugger(debugger), mID(id)
+	{
+	}
+	virtual void completed(bool success)
+	{
+		mDebugger->callbackCacheWrite(mID, success);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerDecodeResponder : public LLImageDecodeThread::Responder
+{
+public:
+	LLDebuggerDecodeResponder(LLTextureFetchDebugger* debugger, S32 id)
+		: mDebugger(debugger), mID(id)
+	{
+	}
+	virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
+	{
+		mDebugger->callbackDecoded(mID, success, raw, aux);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) :
+	mFetcher(fetcher),
+	mTextureCache(cache),
+	mImageDecodeThread(imagedecodethread),
+	mCurlGetRequest(NULL)
+{
+	init();
+}
+	
+LLTextureFetchDebugger::~LLTextureFetchDebugger()
+{
+	mFetchingHistory.clear();
+	stopDebug();
+}
+
+void LLTextureFetchDebugger::init()
+{
+	mState = IDLE;
+	
+	mCacheReadTime = -1.f;
+	mCacheWriteTime = -1.f;
+	mDecodingTime = -1.f;
+	mHTTPTime = -1.f;
+	mGLCreationTime = -1.f;
+	mTotalFetchingTime = 0.f;
+
+	mNumFetchedTextures = 0;
+	mNumCacheHits = 0;
+	mNumVisibleFetchedTextures = 0;
+	mNumVisibleFetchingRequests = 0;
+	mFetchedData = 0;
+	mDecodedData = 0;
+	mVisibleFetchedData = 0;
+	mVisibleDecodedData = 0;
+	mRenderedData = 0;
+	mRenderedDecodedData = 0;
+}
+
+void LLTextureFetchDebugger::startDebug()
+{
+	//lock the fetcher
+	mFetcher->lockFetcher(true);
+
+	//clear the current fetching queue
+	gTextureList.clearFetchingRequests();
+
+	//wait for all works to be done
+	while(1)
+	{
+		S32 pending = 0;
+		pending += LLAppViewer::getTextureCache()->update(1); 
+		pending += LLAppViewer::getImageDecodeThread()->update(1); 
+		pending += LLAppViewer::getTextureFetch()->update(1); 
+		if(!pending)
+		{
+			break;
+		}
+	}
+
+	//collect statistics
+	mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+	
+	std::set<LLUUID> fetched_textures;
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size; i++)
+	{
+		bool in_list = true;
+		if(fetched_textures.find(mFetchingHistory[i].mID) == fetched_textures.end())
+		{
+			fetched_textures.insert(mFetchingHistory[i].mID);
+			in_list = false;
+		}
+		
+		LLViewerFetchedTexture* tex = LLViewerTextureManager::findFetchedTexture(mFetchingHistory[i].mID);
+		if(tex && tex->isJustBound()) //visible
+		{
+			if(!in_list)
+			{
+				mNumVisibleFetchedTextures++;
+			}
+			mNumVisibleFetchingRequests++;
+	
+			mVisibleFetchedData += mFetchingHistory[i].mFetchedSize;
+			mVisibleDecodedData += mFetchingHistory[i].mDecodedSize;
+	
+			if(tex->getDiscardLevel() >= mFetchingHistory[i].mDecodedLevel)
+			{
+				mRenderedData += mFetchingHistory[i].mFetchedSize;
+				mRenderedDecodedData += mFetchingHistory[i].mDecodedSize;
+			}
+		}
+	}
+
+	mNumFetchedTextures = fetched_textures.size();
+}
+
+void LLTextureFetchDebugger::stopDebug()
+{
+	//clear the current debug work
+	S32 size = mFetchingHistory.size();
+	switch(mState)
+	{
+	case READ_CACHE:		
+		for(S32 i = 0 ; i < size; i++)
+		{
+			if (mFetchingHistory[i]. mCacheHandle != LLTextureCache::nullHandle())
+			{
+				mTextureCache->readComplete(mFetchingHistory[i].mCacheHandle, true);
+			}
+		}	
+		break;
+	case WRITE_CACHE:
+		for(S32 i = 0 ; i < size; i++)
+		{
+			if (mFetchingHistory[i].mCacheHandle != LLTextureCache::nullHandle())
+			{
+				mTextureCache->writeComplete(mFetchingHistory[i].mCacheHandle, true);
+			}
+		}
+		break;
+	case DECODING:
+		break;
+	case HTTP_FETCHING:
+		break;
+	case GL_TEX:
+		break;
+	default:
+		break;
+	}
+
+	while(1)
+	{
+		if(update())
+		{
+			break;
+		}
+	}
+
+	//unlock the fetcher
+	mFetcher->lockFetcher(false);
+}
+
+//called in the main thread and when the fetching queue is empty
+void LLTextureFetchDebugger::clearHistory()
+{
+	mFetchingHistory.clear();
+	init();
+}
+
+void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker)
+{
+	if(worker->mInCache)
+	{
+		mNumCacheHits++;
+	}
+	mFetchedData += worker->mFormattedImage->getDataSize();
+	mDecodedData += worker->mRawImage->getDataSize();
+
+	mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize()));
+	//mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mHaveAllData ? 0 : worker->mLoadedDiscard, worker->mFormattedImage->getComponents(),
+		//worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize()));
+}
+
+void LLTextureFetchDebugger::lockCache()
+{
+}
+	
+void LLTextureFetchDebugger::unlockCache()
+{
+}
+	
+void LLTextureFetchDebugger::debugCacheRead()
+{
+	lockCache();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = READ_CACHE;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		mFetchingHistory[i].mFormattedImage = NULL;
+		mFetchingHistory[i].mCacheHandle = mTextureCache->readFromCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, 0, mFetchingHistory[i].mFetchedSize, 
+			new LLDebuggerCacheReadResponder(this, i, mFetchingHistory[i].mFormattedImage));
+	}
+}
+	
+void LLTextureFetchDebugger::debugCacheWrite()
+{
+	//remove from cache
+	S32 size = mFetchingHistory.size();
+	{
+		std::set<LLUUID> deleted_list;
+		for(S32 i = 0 ; i < size ; i++)
+		{
+			if(deleted_list.find(mFetchingHistory[i].mID) == deleted_list.end())
+			{
+				deleted_list.insert(mFetchingHistory[i].mID);
+				mTextureCache->removeFromCache(mFetchingHistory[i].mID);
+			}
+		}
+	}
+
+	lockCache();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = WRITE_CACHE;
+
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		mFetchingHistory[i].mCacheHandle = mTextureCache->writeToCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, 
+			mFetchingHistory[i].mFormattedImage->getData(), mFetchingHistory[i].mFetchedSize,
+			mFetchingHistory[i].mDecodedLevel == 0 ? mFetchingHistory[i].mFetchedSize : mFetchingHistory[i].mFetchedSize + 1, 
+			new LLDebuggerCacheWriteResponder(this, i));					
+	}
+}
+
+void LLTextureFetchDebugger::lockDecoder()
+{
+}
+	
+void LLTextureFetchDebugger::unlockDecoder()
+{
+}
+
+void LLTextureFetchDebugger::debugDecoder()
+{
+	lockDecoder();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = DECODING;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		if(mFetchingHistory[i].mFormattedImage.isNull())
+		{
+			continue;
+		}
+
+		mImageDecodeThread->decodeImage(mFetchingHistory[i].mFormattedImage, LLWorkerThread::PRIORITY_NORMAL, 
+			mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mNeedsAux,
+			new LLDebuggerDecodeResponder(this, i));
+	}
+}
+
+void LLTextureFetchDebugger::debugHTTP()
+{
+	llinfos << "debug HTTP" << llendl;
+}
+
+void LLTextureFetchDebugger::debugGLTextureCreation()
+{
+	llassert_always(mState == IDLE);
+	mState = GL_TEX;
+	std::vector<LLViewerFetchedTexture*> tex_list;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{
+		if(mFetchingHistory[i].mRawImage.notNull())
+		{
+			LLViewerFetchedTexture* tex = gTextureList.findImage(mFetchingHistory[i].mID) ;
+			if(tex && !tex->isForSculptOnly())
+			{
+				tex->destroyGLTexture() ;
+				tex_list.push_back(tex);
+			}
+		}
+	}
+
+	mTimer.reset();
+	S32 j = 0 ;
+	for(S32 i = 0 ; i < size ; i++)
+	{
+		if(mFetchingHistory[i].mRawImage.notNull())
+		{
+			if(mFetchingHistory[i].mID == tex_list[j]->getID())
+			{
+				tex_list[j]->createGLTexture(mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mRawImage, 0, TRUE, tex_list[j]->getBoostLevel());
+				j++;
+			}
+		}
+	}
+
+	mGLCreationTime = mTimer.getElapsedTimeF32() ;
+	return;
+}
+
+bool LLTextureFetchDebugger::update()
+{
+	switch(mState)
+	{
+	case READ_CACHE:
+		if(!mTextureCache->update(1))
+		{
+			mCacheReadTime = mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockCache();
+		}
+		break;
+	case WRITE_CACHE:
+		if(!mTextureCache->update(1))
+		{
+			mCacheWriteTime = mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockCache();
+		}
+		break;
+	case DECODING:
+		if(!mImageDecodeThread->update(1))
+		{
+			mDecodingTime =  mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockDecoder();
+		}
+		break;
+	case HTTP_FETCHING:
+		mState = IDLE;
+		break;
+	case GL_TEX:
+		mState = IDLE;
+		break;
+	default:
+		mState = IDLE;
+		break;
+	}
+
+	return mState == IDLE;
+}
+
+void LLTextureFetchDebugger::callbackCacheRead(S32 id, bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal)
+{
+	if (success)
+	{
+		mFetchingHistory[id].mFormattedImage = image;
+	}
+	mTextureCache->readComplete(mFetchingHistory[id].mCacheHandle, false);
+	mFetchingHistory[id].mCacheHandle = LLTextureCache::nullHandle();
+}
+
+void LLTextureFetchDebugger::callbackCacheWrite(S32 id, bool success)
+{
+	mTextureCache->writeComplete(mFetchingHistory[id].mCacheHandle);
+	mFetchingHistory[id].mCacheHandle = LLTextureCache::nullHandle();
+}
+
+void LLTextureFetchDebugger::callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux)
+{
+	if (success)
+	{
+		llassert_always(raw);
+		mFetchingHistory[id].mRawImage = raw;
+	}
+}
+
+//---------------------
+///////////////////////////////////////////////////////////////////////////////////////////
+//End LLTextureFetchDebugger
+///////////////////////////////////////////////////////////////////////////////////////////
 
 
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 710dd67e99a..529a2e68347 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -34,14 +34,17 @@
 #include "llcurl.h"
 #include "lltextureinfo.h"
 #include "llapr.h"
+#include "llimageworker.h"
+//#include "lltexturecache.h"
 
 class LLViewerTexture;
 class LLTextureFetchWorker;
 class HTTPGetResponder;
-class LLTextureCache;
 class LLImageDecodeThread;
 class LLHost;
 class LLViewerAssetStats;
+class LLTextureFetchDebugger;
+class LLTextureCache;
 
 // Interface class
 class LLTextureFetch : public LLWorkerThread
@@ -212,7 +215,150 @@ class LLTextureFetch : public LLWorkerThread
 	// attempt to log metrics follows a break in the metrics stream
 	// reporting due to either startup or a problem POSTing data.
 	static volatile bool svMetricsDataBreak;
+
+private:
+	//debug use
+	LLTextureFetchDebugger* mFetchDebugger;
+	bool mFetcherLocked;
+
+public:
+	//debug use
+	LLTextureFetchDebugger* getFetchDebugger() { return mFetchDebugger;}
+	void lockFetcher(bool lock) { mFetcherLocked = lock;}
 };
 
+//debug use
+class LLTextureFetchDebugger
+{
+public:
+	LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) ;
+	~LLTextureFetchDebugger();
+
+public:
+	enum e_debug_state
+	{
+		IDLE = 0,
+		READ_CACHE,
+		WRITE_CACHE,
+		DECODING,
+		HTTP_FETCHING,
+		GL_TEX,
+		INVALID
+	};
+
+private:	
+	struct FetchEntry
+	{
+		LLUUID mID;
+		//S32 mRequestedSize;
+		//S32 mFetchedDiscard;
+		//S32 mComponents;
+		S32 mDecodedLevel;
+		S32 mFetchedSize;
+		S32 mDecodedSize;
+		BOOL mNeedsAux;
+		U32 mCacheHandle;
+		LLPointer<LLImageFormatted> mFormattedImage;
+		LLPointer<LLImageRaw> mRawImage;
+
+		FetchEntry() :
+			mDecodedLevel(-1),
+			mFetchedSize(0),
+			mDecodedSize(0)
+			{}
+		FetchEntry(LLUUID& id, /*S32 r_size, S32 f_discard, S32 c,*/ S32 level, S32 f_size, S32 d_size) :
+			mID(id),
+			//mRequestedSize(r_size),
+			//mFetchedDiscard(f_discard),
+			//mComponents(c),
+			mDecodedLevel(level),
+			mFetchedSize(f_size),
+			mDecodedSize(d_size),
+			mNeedsAux(false)
+			{}
+	};
+	std::vector<FetchEntry> mFetchingHistory;
+	
+	e_debug_state mState;
+	
+	F32 mCacheReadTime;
+	F32 mCacheWriteTime;
+	F32 mDecodingTime;
+	F32 mHTTPTime;
+	F32 mGLCreationTime;
+
+	LLTimer mTimer;
+	
+	LLTextureFetch* mFetcher;
+	LLTextureCache* mTextureCache;
+	LLImageDecodeThread* mImageDecodeThread;
+	LLCurlRequest* mCurlGetRequest;
+
+	F32 mTotalFetchingTime;
+	S32 mNumFetchedTextures;
+	S32 mNumCacheHits;
+	S32 mNumVisibleFetchedTextures;
+	S32 mNumVisibleFetchingRequests;
+	U32 mFetchedData;
+	U32 mDecodedData;
+	U32 mVisibleFetchedData;
+	U32 mVisibleDecodedData;
+	U32 mRenderedData;
+	U32 mRenderedDecodedData;
+
+public:
+	bool update(); //called in the main thread once per frame
+
+	//fetching history
+	void clearHistory();
+	void addHistoryEntry(LLTextureFetchWorker* worker);
+	
+	void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;}
+	
+	void startDebug();
+	void stopDebug(); //stop everything
+	void debugCacheRead();
+	void debugCacheWrite();	
+	void debugHTTP();
+	void debugDecoder();
+	void debugGLTextureCreation();
+
+	void callbackCacheRead(S32 id, bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal);
+	void callbackCacheWrite(S32 id, bool success);
+	void callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux);
+
+	e_debug_state getState()             {return mState;}
+	S32  getNumFetchedTextures()         {return mNumFetchedTextures;}
+	S32  getNumFetchingRequests()        {return mFetchingHistory.size();}
+	S32  getNumCacheHits()               {return mNumCacheHits;}
+	S32  getNumVisibleFetchedTextures()  {return mNumVisibleFetchedTextures;}
+	S32  getNumVisibleFetchingRequests() {return mNumVisibleFetchingRequests;}
+	U32  getFetchedData()                {return mFetchedData;}
+	U32  getDecodedData()                {return mDecodedData;}
+	U32  getVisibleFetchedData()         {return mVisibleFetchedData;}
+	U32  getVisibleDecodedData()         {return mVisibleDecodedData;}
+	U32  getRenderedData()               {return mRenderedData;}
+	U32  getRenderedDecodedData()        {return mRenderedDecodedData;}
+
+	F32  getCacheReadTime()     {return mCacheReadTime;}
+	F32  getCacheWriteTime()    {return mCacheWriteTime;}
+	F32  getDecodeTime()        {return mDecodingTime;}
+	F32  getGLCreationTime()    {return mGLCreationTime;}
+	F32  getHTTPTime()          {return mHTTPTime;}
+	F32  getTotalFetchingTime() {return mTotalFetchingTime;}
+
+private:
+	void init();
+
+	void lockFetcher();
+	void unlockFetcher();
+
+	void lockCache();
+	void unlockCache();
+
+	void lockDecoder();
+	void unlockDecoder();
+};
 #endif // LL_LLTEXTUREFETCH_H
 
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index bb870f76514..986b8ac3c5f 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -103,6 +103,7 @@
 #include "llfloatertelehub.h"
 #include "llfloatertestinspectors.h"
 #include "llfloatertestlistview.h"
+#include "llfloatertexturefetchdebugger.h"
 #include "llfloatertools.h"
 #include "llfloatertos.h"
 #include "llfloatertopobjects.h"
@@ -227,6 +228,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>);
 	
 	LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMemLeak>);
+	LLFloaterReg::add("tex_fetch_debugger", "floater_texture_fetch_debugger.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTextureFetchDebugger>);
 	LLFloaterReg::add("media_settings", "floater_media_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMediaSettings>);	
 	LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
 	LLFloaterReg::add("message_tos", "floater_tos.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index d5b53f3cb12..49faae3fdb6 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -178,7 +178,12 @@ LLViewerTexture*  LLViewerTextureManager::findTexture(const LLUUID& id)
 	}
 	return tex ;
 }
-		
+
+LLViewerFetchedTexture*  LLViewerTextureManager::findFetchedTexture(const LLUUID& id) 
+{
+	return gTextureList.findImage(id);
+}
+
 LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id)
 {
 	return LLViewerMediaTexture::findMediaTexture(media_id) ;	
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 6ddff3e4853..d0bc534c5af 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -701,6 +701,7 @@ class LLViewerTextureManager
 	//"find-texture" just check if the texture exists, if yes, return it, otherwise return null.
 	//
 	static LLViewerTexture*           findTexture(const LLUUID& id) ;
+	static LLViewerFetchedTexture*    findFetchedTexture(const LLUUID& id) ;
 	static LLViewerMediaTexture*      findMediaTexture(const LLUUID& id) ;
 	
 	static LLViewerMediaTexture*      createMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ;
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 64e2c1f7912..7038ea24ce1 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -109,6 +109,8 @@ class LLViewerTextureList
 	void doPreloadImages();
 	void doPrefetchImages();
 
+	void clearFetchingRequests();
+
 	static S32 getMinVideoRamSetting();
 	static S32 getMaxVideoRamSetting(bool get_recommended = false);
 	
@@ -163,9 +165,7 @@ class LLViewerTextureList
 	// Request image from a specific host, used for baked avatar textures.
 	// Implemented in header in case someone changes default params above. JC
 	LLViewerFetchedTexture* getImageFromHost(const LLUUID& image_id, LLHost host)
-	{ return getImage(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); }
-	
-	void clearFetchingRequests();
+	{ return getImage(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); }	
 
 public:
 	typedef std::set<LLPointer<LLViewerFetchedTexture> > image_list_t;	
diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
new file mode 100644
index 00000000000..ff7528eeb70
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_minimize="false"
+ height="480"
+ layout="topleft"
+ name="TexFetchDebugger"
+ help_topic="texfetchdebugger"
+ title="Texture Fetching Debugger"
+ width="500">
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left="10"
+   name="total_num_fetched_label"
+   top="30"
+   width="400">
+    1, Total number of fetched textures: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_fetching_requests_label"
+   top_delta="25"
+   width="400">
+    2, Total number of fetching requests: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_cache_hits_label"
+   top_delta="25"
+   width="400">
+    3, Total number of cache hits: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_visible_tex_label"
+   top_delta="25"
+   width="400">
+    4, Total number of visible textures: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_visible_tex_fetch_req_label"
+   top_delta="25"
+   width="450">
+    5, Total number of visible texture fetching requests: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_data_label"
+   top_delta="25"
+   width="480">
+    6, Total number of fetched data/Decoded Data: [SIZE1]KB / [SIZE2]KB
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_vis_data_label"
+   top_delta="25"
+   width="480">
+    7, Total number of visible fetched data/Decoded Data: [SIZE1]KB / [SIZE2]KB
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_rendered_data_label"
+   top_delta="25"
+   width="480">
+    8, Total number of rendered fetched data/Decoded Data: [SIZE1]KB / [SIZE2]KB
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_cache_read_label"
+   top_delta="25"
+   width="400">
+    9, Total time on cache readings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_cache_write_label"
+   top_delta="25"
+   width="400">
+    10, Total time on cache writings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_decode_label"
+   top_delta="25"
+   width="400">
+    11, Total time on decodings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_gl_label"
+   top_delta="25"
+   width="400">
+    12, Total time on gl texture creation: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_http_label"
+   top_delta="25"
+   width="400">
+    13, Total time on HTTP fetching: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_fetch_label"
+   top_delta="25"
+   width="400">
+    14, Total time on entire fetching: [TIME] seconds
+  </text>
+  <spinner
+     decimal_digits="1"
+     follows="left|top"
+     height="20"
+     increment="0.1"
+     initial_value="1.0"
+     label="15, Ratio of Texel/Pixel:"
+     label_width="130"
+     layout="topleft"
+     left_delta="0"
+     max_val="128.0"
+     name="texel_pixel_ratio"
+     top_delta="30"
+     width="200">
+    <spinner.commit_callback
+		function="TexFetchDebugger.ChangeTexelPixelRatio" />
+  </spinner>
+  <button
+   follows="left|top"
+   height="20"
+   label="Start"
+   layout="topleft"
+   left_delta="0"
+   name="start_btn"
+   top_delta="30"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Start" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Reset"
+   layout="topleft"
+   left_pad="7"
+   name="clear_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Clear" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Close"
+   layout="topleft"
+   left_pad="7"
+   name="close_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Close" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Cache Read"
+   layout="topleft"
+   left="10"
+   name="cacheread_btn"
+   top_delta="30"
+   width="80">
+    <button.commit_callback
+		function="TexFetchDebugger.CacheRead" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Cache Write"
+   layout="topleft"
+   left_pad="7"
+   name="cachewrite_btn"
+   top_delta="0"
+   width="80">
+    <button.commit_callback
+		function="TexFetchDebugger.CacheWrite" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="HTTP"
+   layout="topleft"
+   left_pad="7"
+   name="http_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.HTTPLoad" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Decode"
+   layout="topleft"
+   left_pad="7"
+   name="decode_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Decode" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="GL Texture"
+   layout="topleft"
+   left_pad="7"
+   name="gl_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.GLTexture" />
+  </button>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 8b0152b1a28..a1f17ffc172 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1877,6 +1877,13 @@
                  function="Advanced.ToggleConsole"
                  parameter="texture" />
             </menu_item_check>
+          <menu_item_call
+          label="Texture Fetch Debug Console"
+          name="Texture Fetch Debug Console">
+            <menu_item_call.on_click
+             function="Floater.Show"
+             parameter="tex_fetch_debugger" />
+          </menu_item_call>
             <menu_item_check
              label="Debug Console"
              name="Debug Console"
-- 
GitLab