diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index ea2ca68e47a7968f33c37da46bd767b335a4c2d2..a6d4a2ae529a913508f2481e5747029663d07745 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -1506,6 +1506,22 @@ BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
 	return LLView::handleHover( x, y, mask );
 }
 
+LLFolderViewItem* LLFolderView::getHoveredItem() const
+{
+	return dynamic_cast<LLFolderViewItem*>(mHoveredItem.get());
+}
+
+void LLFolderView::setHoveredItem(LLFolderViewItem* itemp)
+{
+	if (mHoveredItem.get() != itemp)
+	{
+		if (itemp)
+			mHoveredItem = itemp->getHandle();
+		else
+			mHoveredItem.markDead();
+	}
+}
+
 BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 									 EDragAndDropType cargo_type,
 									 void* cargo_data, 
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
index 6bb5e6c02e0d4a46f3c4260fb2583a4c960deafe..bd38d93b33c671b78484b4a1dfb2812301f09d74 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -144,6 +144,10 @@ class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler
 	// applies filters to control visibility of items
 	virtual void filter( LLFolderViewFilter& filter);
 
+	void              clearHoveredItem() { setHoveredItem(nullptr); }
+	LLFolderViewItem* getHoveredItem() const;
+	void              setHoveredItem(LLFolderViewItem* itemp);
+
 	// Get the last selected item
 	virtual LLFolderViewItem* getCurSelectedItem( void );
     selected_items_t& getSelectedItems( void );
@@ -273,6 +277,7 @@ class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler
 protected:
 	LLHandle<LLView>					mPopupMenuHandle;
 	
+	LLHandle<LLView>				mHoveredItem;
 	selected_items_t				mSelectedItems;
 	bool							mKeyboardSelection,
 									mAllowMultiSelect,
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index eba93beed94b146f406aa87b735b3bd775a54f82..d10130619aa7511dfc84de962a69c4a25ed34dfb 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -624,11 +624,14 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
 			getWindow()->setCursor(UI_CURSOR_NOLOCKED);
 		}
 
+		root->clearHoveredItem();
 		return TRUE;
 	}
 	else
 	{
-		getRoot()->setShowSelectionContext(FALSE);
+		LLFolderView* pRoot = getRoot();
+		pRoot->setHoveredItem(this);
+		pRoot->setShowSelectionContext(FALSE);
 		getWindow()->setCursor(UI_CURSOR_ARROW);
 		// let parent handle this then...
 		return FALSE;
@@ -683,6 +686,13 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
 void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask)
 {
 	mIsMouseOverTitle = false;
+
+	// NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it
+	LLFolderView* pRoot = getRoot();
+	if (this == pRoot->getHoveredItem())
+	{
+		pRoot->clearHoveredItem();
+	}
 }
 
 BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index 2f56a8b1d0bfdd38d6d5d0399e6646037d66d05a..a6552d4ff1c312e68ec89cf81895549f3ef67ba0 100644
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -163,6 +163,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
 :	LLPanel(p),
 	mHasClickCallback(p.click_callback.isProvided()),
 	mPadding(p.padding),
+	mMaxWidth(p.max_width),
 	mTextBox(NULL),
 	mInfoButton(NULL),
 	mPlayMediaButton(NULL),
@@ -272,7 +273,7 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
 
 	// do this *after* we've had our size set in LLPanel::initFromParams();
 	const S32 REALLY_LARGE_HEIGHT = 10000;
-	mTextBox->reshape(p.max_width, REALLY_LARGE_HEIGHT);
+	mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT);
 
 	if (p.styled_message.isProvided())
 	{
@@ -288,16 +289,19 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
 		mTextBox->setText(p.message());
 	}
 
-	S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1);
+	updateTextBox();
+	snapToChildren();
+}
+
+void LLToolTip::updateTextBox()
+{
+	S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1);
 	S32 text_height = mTextBox->getTextPixelHeight();
 	mTextBox->reshape(text_width, text_height);
-	if (mInfoButton)
-	{
-		LLRect text_rect = mTextBox->getRect();
-		LLRect icon_rect = mInfoButton->getRect();
-		mTextBox->translate(0, icon_rect.getCenterY() - text_rect.getCenterY());
-	}
-
+}
+ 
+void LLToolTip::snapToChildren()
+{
 	// reshape tooltip panel to fit text box
 	LLRect tooltip_rect = calcBoundingRect();
 	tooltip_rect.mTop += mPadding;
@@ -305,7 +309,14 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
 	tooltip_rect.mBottom = 0;
 	tooltip_rect.mLeft = 0;
 
-	mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
+	if (mInfoButton)
+	{
+		mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
+
+		LLRect text_rect = mTextBox->getRect();
+		LLRect icon_rect = mInfoButton->getRect();
+		mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY());
+	}
 
 	setShape(tooltip_rect);
 }
@@ -428,7 +439,10 @@ void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
 	}
 	tooltip_params.rect = LLRect (0, 1, 1, 0);
 
-	mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
+	if (tooltip_params.create_callback.isProvided())
+		mToolTip = tooltip_params.create_callback()(tooltip_params);
+	else
+		mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
 
 	gToolTipView->addChild(mToolTip);
 
diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h
index 0b1fbe536748946108178440b823c9421846c394..86943625fff61184201e7ac06fd21436bd87f2c7 100644
--- a/indra/llui/lltooltip.h
+++ b/indra/llui/lltooltip.h
@@ -68,6 +68,7 @@ class LLToolTip : public LLPanel
 	struct Params : public LLInitParam::Block<Params, LLPanel::Params> 
 	{
 		typedef boost::function<void(void)> click_callback_t;
+		typedef boost::function<LLToolTip*(LLToolTip::Params)> create_callback_t;
 
 		Optional<std::string>		message;
 		Multiple<StyledText>		styled_message;
@@ -84,6 +85,8 @@ class LLToolTip : public LLPanel
 		Optional<bool>				time_based_media,
 									web_based_media,
 									media_playing;
+		Optional<create_callback_t>	create_callback;
+		Optional<LLSD>				create_params;
 		Optional<click_callback_t>	click_callback,
 									click_playmedia_callback,
 									click_homepage_callback;
@@ -103,11 +106,15 @@ class LLToolTip : public LLPanel
 	bool hasClickCallback();
 
 	LLToolTip(const Params& p);
-	void initFromParams(const LLToolTip::Params& params);
+	virtual void initFromParams(const LLToolTip::Params& params);
 
 	void getToolTipMessage(std::string & message);
 
-private:
+protected:
+	void updateTextBox();
+	void snapToChildren();
+
+protected:
 	class LLTextBox*	mTextBox;
 	class LLButton*     mInfoButton;
 	class LLButton*     mPlayMediaButton;
@@ -117,6 +124,7 @@ class LLToolTip : public LLPanel
 	LLFrameTimer	mVisibleTimer;
 	bool			mHasClickCallback;
 	S32				mPadding;	// pixels
+	S32				mMaxWidth;
 };
 
 // used for the inspector tooltips which need different background images etc.
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 9eee5338ece9a4ef59db4f43d963e4c8821edeb0..b94a1415a216accfd6c419e0401c32abc0c77a46 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -374,6 +374,7 @@ set(viewer_SOURCE_FILES
     llinspectgroup.cpp
     llinspectobject.cpp
     llinspectremoteobject.cpp
+    llinspecttexture.cpp
     llinspecttoast.cpp
     llinventorybridge.cpp
     llinventoryfilter.cpp
@@ -1014,6 +1015,7 @@ set(viewer_HEADER_FILES
     llinspectgroup.h
     llinspectobject.h
     llinspectremoteobject.h
+    llinspecttexture.h
     llinspecttoast.h
     llinventorybridge.h
     llinventoryfilter.h
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index c4c88d304c09bf5e841f880f66de9d805a67b55d..e6b6b1040816ecef6b12f134ee19cdd4aa887a50 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -78,7 +78,9 @@ static S32 cube_channel = -1;
 static S32 diffuse_channel = -1;
 static S32 bump_channel = -1;
 
-#define LL_BUMPLIST_MULTITHREADED 0 // TODO -- figure out why this doesn't work
+// Enabled after changing LLViewerTexture::mNeedsCreateTexture to an
+// LLAtomicBool; this should work just fine, now. HB
+#define LL_BUMPLIST_MULTITHREADED 1
 
 
 // static 
diff --git a/indra/newview/llinspecttexture.cpp b/indra/newview/llinspecttexture.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cd288e9603e61f583899fd8a2d896d4affbb80b8
--- /dev/null
+++ b/indra/newview/llinspecttexture.cpp
@@ -0,0 +1,301 @@
+/**
+ * @file llinspecttexture.cpp
+ *
+ * $LicenseInfo:firstyear=2009&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 "llfloaterreg.h"
+#include "llinspect.h"
+#include "llinspecttexture.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "lltexturectrl.h"
+#include "lltrans.h"
+#include "llviewertexturelist.h"
+
+// ============================================================================
+// LLInspectTexture class
+//
+
+class LLInspectTexture : public LLInspect
+{
+	friend class LLFloaterReg;
+public:
+	LLInspectTexture(const LLSD& sdKey);
+	~LLInspectTexture();
+
+public:
+	void onOpen(const LLSD& sdData) override;
+	BOOL postBuild() override;
+
+public:
+	const LLUUID& getAssetId() const { return mAssetId; }
+	const LLUUID& getItemId() const  { return mItemId; }
+
+protected:
+	LLUUID         mAssetId;
+	LLUUID         mItemId;		// Item UUID relative to gInventoryModel (or null if not displaying an inventory texture)
+	LLUUID         mNotecardId;
+	LLTextureCtrl* mTextureCtrl = nullptr;
+	LLTextBox*     mTextureName = nullptr;
+};
+
+LLInspectTexture::LLInspectTexture(const LLSD& sdKey)
+	: LLInspect(LLSD())
+{
+}
+
+LLInspectTexture::~LLInspectTexture()
+{
+}
+
+void LLInspectTexture::onOpen(const LLSD& sdData)
+{
+	// Start fade animation
+	LLInspect::onOpen(sdData);
+
+	bool fIsAsset = sdData.has("asset_id");
+	bool fIsInventory = sdData.has("item_id");
+
+	// Skip if we're being asked to display the same thing
+	const LLUUID idAsset = (fIsAsset) ? sdData["asset_id"].asUUID() : LLUUID::null;
+	const LLUUID idItem = (fIsInventory) ? sdData["item_id"].asUUID() : LLUUID::null;
+	if ( (getVisible()) && ( ((fIsAsset) && (idAsset == mAssetId)) || ((fIsInventory) && (idItem == mItemId)) ) )
+	{
+		return;
+	}
+
+	// Position the inspector relative to the mouse cursor
+	// Similar to how tooltips are positioned [see LLToolTipMgr::createToolTip()]
+	if (sdData.has("pos"))
+		LLUI::instance().positionViewNearMouse(this, sdData["pos"]["x"].asInteger(), sdData["pos"]["y"].asInteger());
+	else
+		LLUI::instance().positionViewNearMouse(this);
+
+	std::string strName = sdData["name"].asString();
+	if (fIsAsset)
+	{
+		mAssetId = idAsset;
+		mItemId = idItem;		// Will be non-null in the case of a notecard
+		mNotecardId = sdData["notecard_id"].asUUID();
+	}
+	else if (fIsInventory)
+	{
+		const LLViewerInventoryItem* pItem = gInventory.getItem(idItem);
+		if ( (pItem) && (LLAssetType::AT_TEXTURE == pItem->getType()) )
+		{
+			if (strName.empty())
+				strName = pItem->getName();
+			mAssetId = pItem->getAssetUUID();
+			mItemId = idItem;
+		}
+		else
+		{
+			mAssetId.setNull();
+			mItemId.setNull();
+		}
+		mNotecardId = LLUUID::null;
+	}
+
+	mTextureCtrl->setImageAssetID(mAssetId);
+	mTextureName->setText(strName);
+}
+
+BOOL LLInspectTexture::postBuild()
+{
+	mTextureCtrl = getChild<LLTextureCtrl>("texture_ctrl");
+	mTextureName = getChild<LLTextBox>("texture_name");
+
+	return TRUE;
+}
+
+// ============================================================================
+// Helper functions
+//
+
+LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p)
+{
+	const LLSD& sdTooltip = p.create_params;
+
+	LLInventoryType::EType eInvType = (sdTooltip.has("inv_type")) ? (LLInventoryType::EType)sdTooltip["inv_type"].asInteger() : LLInventoryType::IT_NONE;
+	switch (eInvType)
+	{
+		case LLInventoryType::IT_SNAPSHOT:
+		case LLInventoryType::IT_TEXTURE:
+			return LLUICtrlFactory::create<LLTextureToolTip>(p);
+		case LLInventoryType::IT_CATEGORY:
+			{
+				if (sdTooltip.has("item_id"))
+				{
+					const LLUUID idCategory = sdTooltip["item_id"].asUUID();
+
+					LLInventoryModel::cat_array_t cats;
+					LLInventoryModel::item_array_t items;
+					LLIsOfAssetType f(LLAssetType::AT_TEXTURE);
+					gInventory.getDirectDescendentsOf(idCategory, cats, items, f);
+
+					// Exactly one texture found => show the texture tooltip
+					if (1 == items.size())
+					{
+						p.create_params.getValue()["asset_id"] = items.front()->getAssetUUID();
+						return LLUICtrlFactory::create<LLTextureToolTip>(p);
+					}
+				}
+
+				// No or more than one texture found => show default tooltip
+				return LLUICtrlFactory::create<LLToolTip>(p);
+			}
+		default:
+			return LLUICtrlFactory::create<LLToolTip>(p);
+	}
+}
+
+void LLInspectTextureUtil::registerFloater()
+{
+	LLFloaterReg::add("inspect_texture", "inspect_texture.xml", &LLFloaterReg::build<LLInspectTexture>);
+}
+
+// ============================================================================
+// LLTexturePreviewView helper class
+//
+
+class LLTexturePreviewView : public LLView
+{
+public:
+	LLTexturePreviewView(const LLView::Params& p);
+	~LLTexturePreviewView();
+
+public:
+	void draw() override;
+
+public:
+	void setImageFromAssetId(const LLUUID& idAsset);
+	void setImageFromItemId(const LLUUID& idItem);
+
+protected:
+	LLPointer<LLViewerFetchedTexture> m_Image;
+	S32         mImageBoostLevel = LLGLTexture::BOOST_NONE;
+	std::string mLoadingText;
+};
+
+
+LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p)
+	: LLView(p)
+{
+	mLoadingText = LLTrans::getString("texture_loading");
+}
+
+LLTexturePreviewView::~LLTexturePreviewView()
+{
+	if (m_Image)
+	{
+		m_Image->setBoostLevel(mImageBoostLevel);
+		m_Image = nullptr;
+	}
+}
+
+void LLTexturePreviewView::draw()
+{
+	if (m_Image)
+	{
+		LLRect rctClient = getLocalRect();
+
+		gl_rect_2d(rctClient, LLColor4::black);
+		rctClient.stretch(-2);
+		if (4 == m_Image->getComponents())
+			gl_rect_2d_checkerboard(rctClient);
+		gl_draw_scaled_image(rctClient.mLeft, rctClient.mBottom, rctClient.getWidth(), rctClient.getHeight(), m_Image);
+
+		bool isLoading = (!m_Image->isFullyLoaded()) && (m_Image->getDiscardLevel() > 0);
+		if (isLoading)
+			LLFontGL::getFontSansSerif()->renderUTF8(mLoadingText, 0, llfloor(rctClient.mLeft + 3),  llfloor(rctClient.mTop - 25), LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
+		m_Image->addTextureStats((isLoading) ? MAX_IMAGE_AREA : (F32)(rctClient.getWidth() * rctClient.getHeight()));
+	}
+}
+
+void LLTexturePreviewView::setImageFromAssetId(const LLUUID& idAsset)
+{
+	m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+	if (m_Image)
+	{
+		mImageBoostLevel = m_Image->getBoostLevel();
+		m_Image->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+		m_Image->forceToSaveRawImage(0);
+		if ( (!m_Image->isFullyLoaded()) && (!m_Image->hasFetcher()) )
+		{
+			if (m_Image->isInFastCacheList())
+			{
+				m_Image->loadFromFastCache();
+			}
+			gTextureList.forceImmediateUpdate(m_Image);
+		}
+	}
+}
+
+void LLTexturePreviewView::setImageFromItemId(const LLUUID& idItem)
+{
+	const LLViewerInventoryItem* pItem = gInventory.getItem(idItem);
+	setImageFromAssetId( (pItem) ? pItem->getAssetUUID() : LLUUID::null );
+}
+
+// ============================================================================
+// LLTextureToolTip class
+//
+
+LLTextureToolTip::LLTextureToolTip(const LLToolTip::Params& p)
+	: LLToolTip(p)
+	, mPreviewView(nullptr)
+	, mPreviewSize(256)
+{
+	mMaxWidth = llmax(mMaxWidth, mPreviewSize);
+}
+
+LLTextureToolTip::~LLTextureToolTip()
+{
+}
+
+void LLTextureToolTip::initFromParams(const LLToolTip::Params& p)
+{
+	LLToolTip::initFromParams(p);
+
+	// Create and add the preview control
+	LLView::Params p_preview;
+	p_preview.name = "texture_preview";
+	LLRect rctPreview;
+	rctPreview.setOriginAndSize(mPadding, mTextBox->getRect().mTop, mPreviewSize, mPreviewSize);
+	p_preview.rect = rctPreview;
+	mPreviewView = LLUICtrlFactory::create<LLTexturePreviewView>(p_preview);
+	addChild(mPreviewView);
+
+	// Parse the control params
+	const LLSD& sdTextureParams = p.create_params;
+	if (sdTextureParams.has("asset_id"))
+		mPreviewView->setImageFromAssetId(sdTextureParams["asset_id"].asUUID());
+	else if (sdTextureParams.has("item_id"))
+		mPreviewView->setImageFromItemId(sdTextureParams["item_id"].asUUID());
+
+	snapToChildren();
+}
+
+// ============================================================================
diff --git a/indra/newview/llinspecttexture.h b/indra/newview/llinspecttexture.h
new file mode 100644
index 0000000000000000000000000000000000000000..192aafc3b49715066c155ebca5b6a2c89d42aa1e
--- /dev/null
+++ b/indra/newview/llinspecttexture.h
@@ -0,0 +1,52 @@
+/**
+ * @file llinspecttexture.h
+ *
+ * $LicenseInfo:firstyear=2009&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$
+ */
+
+#pragma once
+
+#include "lltooltip.h"
+
+class LLTexturePreviewView;
+
+namespace LLInspectTextureUtil
+{
+	LLToolTip* createInventoryToolTip(LLToolTip::Params p);
+
+	// Register with LLFloaterReg
+	void registerFloater();
+}
+
+class LLTextureToolTip : public LLToolTip
+{
+public:
+	LLTextureToolTip(const LLToolTip::Params& p);
+	~LLTextureToolTip();
+
+public:
+	void initFromParams(const LLToolTip::Params& p) override;
+
+protected:
+	LLTexturePreviewView* mPreviewView;
+	S32                   mPreviewSize;
+};
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 6ba04cdff25b058ceec4c1956cffef162629aa8b..cbcdf2efb0f3b89aa2c6dba8fc99123ad7835c95 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -451,6 +451,31 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
 	items = get_ptr_in_map(mParentChildItemTree, cat_id);
 }
 
+void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const
+{
+    if (cat_array_t* categoriesp = get_ptr_in_map(mParentChildCategoryTree, cat_id))
+    {
+        for (LLViewerInventoryCategory* pFolder : *categoriesp)
+        {
+			if (f(pFolder, nullptr))
+			{
+				categories.push_back(pFolder);
+			}
+        }
+    }
+
+    if (item_array_t* itemsp = get_ptr_in_map(mParentChildItemTree, cat_id))
+    {
+        for (LLViewerInventoryItem* pItem : *itemsp)
+        {
+			if (f(nullptr, pItem))
+			{
+				items.push_back(pItem);
+			}
+        }
+    }
+}
+
 LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
 {
 	LLInventoryModel::cat_array_t* cat_array;
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index c4133ff9bb050fc0f7cae244c168fe20bc982bb6..b03181d646e151e973da8fb305becf586e097036 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -256,6 +256,7 @@ class LLInventoryModel
 	void getDirectDescendentsOf(const LLUUID& cat_id,
 								cat_array_t*& categories,
 								item_array_t*& items) const;
+    void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const;
 
 	// Compute a hash of direct descendant names (for detecting child name changes)
 	LLMD5 hashDirectDescendentNames(const LLUUID& cat_id) const;
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 6b102c750063f91192ded91699f7af3212d954f4..dabe633b8c3e112a27a158f2054ad466e18843e7 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -40,6 +40,7 @@
 #include "llfolderviewitem.h"
 #include "llfloaterimcontainer.h"
 #include "llimview.h"
+#include "llinspecttexture.h"
 #include "llinventorybridge.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodelbackgroundfetch.h"
@@ -1278,6 +1279,28 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
 	return TRUE;
 }
 
+BOOL LLInventoryPanel::handleToolTip(S32 x, S32 y, MASK mask)
+{
+	if (const LLFolderViewItem* hover_item_p = (!mFolderRoot.isDead()) ? mFolderRoot.get()->getHoveredItem() : nullptr)
+	{
+		if (const LLFolderViewModelItemInventory* vm_item_p = static_cast<const LLFolderViewModelItemInventory*>(hover_item_p->getViewModelItem()))
+		{
+			// Copy/pasted from LLView::handleToolTip()
+			F32 nTimeout = LLToolTipMgr::instance().toolTipVisible()
+					? LLUI::getInstance()->mSettingGroups["config"]->getF32("ToolTipFastDelay")
+					: LLUI::getInstance()->mSettingGroups["config"]->getF32("ToolTipDelay");
+			LLToolTipMgr::instance().show(LLToolTip::Params()
+					.message(hover_item_p->getToolTip())
+					.sticky_rect(hover_item_p->calcScreenRect())
+					.delay_time(nTimeout)
+					.create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
+					.create_params(LLSD().with("inv_type", vm_item_p->getInventoryType()).with("item_id", vm_item_p->getUUID())));
+			return TRUE;
+		}
+	}
+	return LLPanel::handleToolTip(x, y, mask);
+}
+
 BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 								   EDragAndDropType cargo_type,
 								   void* cargo_data,
diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h
index 552c61b915dc69daae071e28f126d37f53d1fd71..d5dc8cdba697b44610109ff239a93e0d0943b0b7 100644
--- a/indra/newview/llinventorypanel.h
+++ b/indra/newview/llinventorypanel.h
@@ -168,6 +168,7 @@ class LLInventoryPanel : public LLPanel
 								   void* cargo_data,
 								   EAcceptance* accept,
 								   std::string& tooltip_msg);
+	            BOOL handleToolTip(S32 x, S32 y, MASK mask) override;
 	// LLUICtrl methods
 	 /*virtual*/ void onFocusLost();
 	 /*virtual*/ void onFocusReceived();
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 59654350e4ef8c8119a96d63bd5264ec1df2bc0b..77a7fd21a81089b5558306d4498f4bf49767c320 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -153,6 +153,7 @@
 #include "llinspectgroup.h"
 #include "llinspectobject.h"
 #include "llinspectremoteobject.h"
+#include "llinspecttexture.h"
 #include "llinspecttoast.h"
 #include "llmoveview.h"
 #include "llfloaterimnearbychat.h"
@@ -281,6 +282,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLInspectAvatarUtil::registerFloater();
 	LLInspectGroupUtil::registerFloater();
 	LLInspectObjectUtil::registerFloater();
+	LLInspectTextureUtil::registerFloater();
 	LLInspectRemoteObjectUtil::registerFloater();
 	LLFloaterVoiceVolumeUtil::registerFloater();
 	LLNotificationsUI::registerFloater();
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
index e2de7ac825e7e5f76b2e4f1bf7097b7438a3f621..299047d91b347a58c905861ca235aba5420b6f24 100644
--- a/indra/newview/llviewertexteditor.cpp
+++ b/indra/newview/llviewertexteditor.cpp
@@ -36,6 +36,7 @@
 #include "llfloatersidepanelcontainer.h"
 #include "llfloaterworldmap.h"
 #include "llfocusmgr.h"
+#include "llinspecttexture.h"
 #include "llinventorybridge.h"
 #include "llinventorydefines.h"
 #include "llinventorymodel.h"
@@ -245,6 +246,16 @@ class LLEmbeddedItemSegment : public LLTextSegment
 	}
 	virtual BOOL				handleToolTip(S32 x, S32 y, MASK mask )
 	{ 
+		if (LLAssetType::AT_TEXTURE == mItem->getType())
+		{
+			LLToolTipMgr::instance().show(LLToolTip::Params()
+					.message(mToolTip)
+					.create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
+					.create_params(LLSD().with("inv_type", mItem->getInventoryType()).with("asset_id", mItem->getAssetUUID())));
+
+			return TRUE;
+		}
+
 		if (!mToolTip.empty())
 		{
 			LLToolTipMgr::instance().show(mToolTip);
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index e3ac56d0d399d4d817adcb540a1a616bf7a6ab9b..8a11c5cf8f38ecf3a29b896851844e6d0af24556 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1118,7 +1118,7 @@ void LLViewerFetchedTexture::init(bool firstinit)
 	mLoadedCallbackDesiredDiscardLevel = S8_MAX;
 	mPauseLoadedCallBacks = FALSE;
 
-	mNeedsCreateTexture = FALSE;
+	mNeedsCreateTexture = false;
 	
 	mIsRawImageValid = FALSE;
 	mRawDiscardLevel = INVALID_DISCARD_LEVEL;
@@ -1400,12 +1400,12 @@ void LLViewerFetchedTexture::addToCreateTexture()
 	{
 		//just update some variables, not to create a real GL texture.
 		createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE);
-		mNeedsCreateTexture = FALSE;
+		mNeedsCreateTexture = false;
 		destroyRawImage();
 	}
 	else if(!force_update && getDiscardLevel() > -1 && getDiscardLevel() <= mRawDiscardLevel)
 	{
-		mNeedsCreateTexture = FALSE;
+		mNeedsCreateTexture = false;
 		destroyRawImage();
 	}
 	else
@@ -1441,7 +1441,7 @@ void LLViewerFetchedTexture::addToCreateTexture()
 						mRawDiscardLevel += i;
 						if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0)
 						{
-							mNeedsCreateTexture = FALSE;
+							mNeedsCreateTexture = false;
 							destroyRawImage();
 							return;
 						}
@@ -1473,7 +1473,7 @@ BOOL LLViewerFetchedTexture::preCreateTexture(S32 usename/*= 0*/)
         destroyRawImage();
         return FALSE;
     }
-    mNeedsCreateTexture = FALSE;
+    mNeedsCreateTexture = false;
 
     if (mRawImage.isNull())
     {
@@ -1609,14 +1609,14 @@ void LLViewerFetchedTexture::postCreateTexture()
         destroyRawImage();
     }
 
-    mNeedsCreateTexture = FALSE;
+    mNeedsCreateTexture = false;
 }
 
 void LLViewerFetchedTexture::scheduleCreateTexture()
 {
     if (!mNeedsCreateTexture)
     {
-        mNeedsCreateTexture = TRUE;
+        mNeedsCreateTexture = true;
         if (preCreateTexture())
         {
 #if LL_IMAGEGL_THREAD_CHECK
@@ -1630,7 +1630,7 @@ void LLViewerFetchedTexture::scheduleCreateTexture()
                 memcpy(data_copy, data, size);
             }
 #endif
-            mNeedsCreateTexture = TRUE;
+            mNeedsCreateTexture = true;
             auto mainq = LLImageGLThread::sEnabled ? mMainQueue.lock() : nullptr;
             if (mainq)
             {
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index b953d7006b26603b993028d63e35406a14523775..2f5e0d01dfd494a4a8198332ac1c7c9fa80f9dc3 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -27,6 +27,7 @@
 #ifndef LL_LLVIEWERTEXTURE_H					
 #define LL_LLVIEWERTEXTURE_H
 
+#include "llatomic.h"
 #include "llgltexture.h"
 #include "lltimer.h"
 #include "llframetimer.h"
@@ -528,7 +529,9 @@ class LLViewerFetchedTexture : public LLViewerTexture
 	LLFrameTimer mStopFetchingTimer;	// Time since mDecodePriority == 0.f.
 
 	BOOL  mInImageList;				// TRUE if image is in list (in which case don't reset priority!)
-	BOOL  mNeedsCreateTexture;	
+	// This needs to be atomic, since it is written both in the main thread
+	// and in the GL image worker thread... HB
+	LLAtomicBool  mNeedsCreateTexture;	
 
 	BOOL   mForSculpt ; //a flag if the texture is used as sculpt data.
 	BOOL   mIsFetched ; //is loaded from remote or from cache, not generated locally.
diff --git a/indra/newview/skins/default/xui/en/inspect_texture.xml b/indra/newview/skins/default/xui/en/inspect_texture.xml
new file mode 100644
index 0000000000000000000000000000000000000000..30be90cfa0111e58c617d7c4b3141900f9783bb2
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/inspect_texture.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<!--
+  Not can_close / no title to avoid window chrome
+  Single instance - only have one at a time, recycle it each spawn
+-->
+<floater
+ legacy_header_height="25"
+ bevel_style="in"
+ bg_opaque_image="Inspector_Background" 
+ can_close="false"
+ can_minimize="false"
+ height="295"
+ layout="topleft"
+ name="inspect_texture"
+ single_instance="true"
+ sound_flags="0"
+ visible="true"
+ width="250">
+  <texture_picker
+   enabled="false"
+   fallback_image="default_land_picture.j2c"
+   follows="all"
+   height="262"
+   layout="topleft"
+   left="5"
+   name="texture_ctrl"
+   top_pad="5"
+   width="240" />
+  <text
+   follows="top|left"
+   font="SansSerifSmall"
+   height="16"
+   left="7"
+   name="texture_name"
+   parse_urls="false"
+   right="-7"
+   top_delta="244"
+   text_color="White"
+   translate="false"
+   use_ellipses="true"
+   word_wrap="true"
+   value="Name of the texture goes here" />
+  <button
+   bottom="-5"
+   follows="bottom|left"
+   height="23"
+   label="Open"
+   left="7"
+   name="open_btn"
+   width="115"
+   commit_callback.function="InspectTexture.Open"
+   enable_callback.function="InspectTexture.CanOpen" />
+  <button
+   bottom="-5"
+   follows="bottom|left"
+   height="23"
+   label="Copy to Inventory"
+   left_pad="5"
+   name="copy_btn"
+   width="115"
+   commit_callback.function="InspectTexture.CopyToInv"
+   enable_callback.function="InspectTexture.CanCopyToInv" />
+</floater>