diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index a8d7df3bc839e75be25a944c49e916a86e9853f8..a3aed4dd8abba758d7247fcbe436ed77b4f3faad 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -127,6 +127,11 @@ PFNGLUNMAPBUFFERARBPROC				glUnmapBufferARB = NULL;
 PFNGLGETBUFFERPARAMETERIVARBPROC	glGetBufferParameterivARB = NULL;
 PFNGLGETBUFFERPOINTERVARBPROC		glGetBufferPointervARB = NULL;
 
+// GL_ARB_map_buffer_range
+PFNGLMAPBUFFERRANGEPROC			glMapBufferRange;
+PFNGLFLUSHMAPPEDBUFFERRANGEPROC	glFlushMappedBufferRange;
+
+
 // vertex object prototypes
 PFNGLNEWOBJECTBUFFERATIPROC			glNewObjectBufferATI = NULL;
 PFNGLISOBJECTBUFFERATIPROC			glIsObjectBufferATI = NULL;
@@ -331,6 +336,7 @@ LLGLManager::LLGLManager() :
 	mHasBlendFuncSeparate(FALSE),
 
 	mHasVertexBufferObject(FALSE),
+	mHasMapBufferRange(FALSE),
 	mHasPBuffer(FALSE),
 	mHasShaderObjects(FALSE),
 	mHasVertexShader(FALSE),
@@ -761,6 +767,7 @@ void LLGLManager::initExtensions()
 	mHasOcclusionQuery = ExtensionExists("GL_ARB_occlusion_query", gGLHExts.mSysExts);
 	mHasOcclusionQuery2 = ExtensionExists("GL_ARB_occlusion_query2", gGLHExts.mSysExts);
 	mHasVertexBufferObject = ExtensionExists("GL_ARB_vertex_buffer_object", gGLHExts.mSysExts);
+	mHasMapBufferRange = ExtensionExists("GL_ARB_map_buffer_range", gGLHExts.mSysExts);
 	mHasDepthClamp = ExtensionExists("GL_ARB_depth_clamp", gGLHExts.mSysExts) || ExtensionExists("GL_NV_depth_clamp", gGLHExts.mSysExts);
 	// mask out FBO support when packed_depth_stencil isn't there 'cause we need it for LLRenderTarget -Brad
 #ifdef GL_ARB_framebuffer_object
@@ -955,6 +962,11 @@ void LLGLManager::initExtensions()
 			mHasVertexBufferObject = FALSE;
 		}
 	}
+	if (mHasMapBufferRange)
+	{
+		glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) GLH_EXT_GET_PROC_ADDRESS("glMapBufferRange");
+		glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) GLH_EXT_GET_PROC_ADDRESS("glFlushMappedBufferRange");
+	}
 	if (mHasFramebufferObject)
 	{
 		llinfos << "initExtensions() FramebufferObject-related procs..." << llendl;
@@ -1411,10 +1423,6 @@ void LLGLState::checkTextureChannels(const std::string& msg)
 		}
 	}
 
-	GLint maxTextureUnits = 0;
-	glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits);
-	stop_glerror();
-
 	static const char* label[] =
 	{
 		"GL_TEXTURE_2D",
@@ -1449,69 +1457,91 @@ void LLGLState::checkTextureChannels(const std::string& msg)
 	glh::matrix4f identity;
 	identity.identity();
 
-	for (GLint i = 1; i < maxTextureUnits; i++)
+	for (GLint i = 1; i < gGLManager.mNumTextureUnits; i++)
 	{
 		gGL.getTexUnit(i)->activate();
-		glClientActiveTextureARB(GL_TEXTURE0_ARB+i);
-		stop_glerror();
-		glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &stackDepth);
-		stop_glerror();
 
-		if (stackDepth != 1)
+		if (i < gGLManager.mNumTextureUnits)
 		{
-			error = TRUE;
-			LL_WARNS("RenderState") << "Texture matrix stack corrupted." << LL_ENDL;
+			glClientActiveTextureARB(GL_TEXTURE0_ARB+i);
+			stop_glerror();
+			glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &stackDepth);
+			stop_glerror();
 
-			if (gDebugSession)
+			if (stackDepth != 1)
 			{
-				gFailLog << "Texture matrix stack corrupted." << std::endl;
+				error = TRUE;
+				LL_WARNS("RenderState") << "Texture matrix stack corrupted." << LL_ENDL;
+
+				if (gDebugSession)
+				{
+					gFailLog << "Texture matrix stack corrupted." << std::endl;
+				}
 			}
-		}
 
-		glGetFloatv(GL_TEXTURE_MATRIX, (GLfloat*) mat.m);
-		stop_glerror();
+			glGetFloatv(GL_TEXTURE_MATRIX, (GLfloat*) mat.m);
+			stop_glerror();
 
-		if (mat != identity)
-		{
-			error = TRUE;
-			LL_WARNS("RenderState") << "Texture matrix in channel " << i << " corrupt." << LL_ENDL;
-			if (gDebugSession)
+			if (mat != identity)
 			{
-				gFailLog << "Texture matrix in channel " << i << " corrupt." << std::endl;
+				error = TRUE;
+				LL_WARNS("RenderState") << "Texture matrix in channel " << i << " corrupt." << LL_ENDL;
+				if (gDebugSession)
+				{
+					gFailLog << "Texture matrix in channel " << i << " corrupt." << std::endl;
+				}
 			}
-		}
 				
-		for (S32 j = (i == 0 ? 1 : 0); 
-			j < 9; j++)
-		{
-			if (j == 8 && !gGLManager.mHasTextureRectangle ||
-				j == 9 && !gGLManager.mHasTextureMultisample)
+			for (S32 j = (i == 0 ? 1 : 0); 
+				j < 9; j++)
 			{
-				continue;
-			}
+				if (j == 8 && !gGLManager.mHasTextureRectangle ||
+					j == 9 && !gGLManager.mHasTextureMultisample)
+				{
+					continue;
+				}
 				
-			if (glIsEnabled(value[j]))
+				if (glIsEnabled(value[j]))
+				{
+					error = TRUE;
+					LL_WARNS("RenderState") << "Texture channel " << i << " still has " << label[j] << " enabled." << LL_ENDL;
+					if (gDebugSession)
+					{
+						gFailLog << "Texture channel " << i << " still has " << label[j] << " enabled." << std::endl;
+					}
+				}
+				stop_glerror();
+			}
+
+			glGetFloatv(GL_TEXTURE_MATRIX, mat.m);
+			stop_glerror();
+
+			if (mat != identity)
 			{
 				error = TRUE;
-				LL_WARNS("RenderState") << "Texture channel " << i << " still has " << label[j] << " enabled." << LL_ENDL;
+				LL_WARNS("RenderState") << "Texture matrix " << i << " is not identity." << LL_ENDL;
 				if (gDebugSession)
 				{
-					gFailLog << "Texture channel " << i << " still has " << label[j] << " enabled." << std::endl;
+					gFailLog << "Texture matrix " << i << " is not identity." << std::endl;
 				}
 			}
-			stop_glerror();
 		}
 
-		glGetFloatv(GL_TEXTURE_MATRIX, mat.m);
-		stop_glerror();
-
-		if (mat != identity)
 		{
-			error = TRUE;
-			LL_WARNS("RenderState") << "Texture matrix " << i << " is not identity." << LL_ENDL;
-			if (gDebugSession)
+			GLint tex = 0;
+			stop_glerror();
+			glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex);
+			stop_glerror();
+
+			if (tex != 0)
 			{
-				gFailLog << "Texture matrix " << i << " is not identity." << std::endl;
+				error = TRUE;
+				LL_WARNS("RenderState") << "Texture channel " << i << " still has texture " << tex << " bound." << llendl;
+
+				if (gDebugSession)
+				{
+					gFailLog << "Texture channel " << i << " still has texture " << tex << " bound." << std::endl;
+				}
 			}
 		}
 	}
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index 420922cf0670e1eb26933aa1cfa0263d8d6ae0d3..d1bee001617b33f0c3447b6571c50cefeff5949b 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -88,6 +88,7 @@ class LLGLManager
 		
 	// ARB Extensions
 	BOOL mHasVertexBufferObject;
+	BOOL mHasMapBufferRange;
 	BOOL mHasPBuffer;
 	BOOL mHasShaderObjects;
 	BOOL mHasVertexShader;
diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
index 825d304d355fdfda2887747a222cfe4ded0f6ca1..94f7a08c925fe6cb3cc13640c79b1597a0aa774d 100644
--- a/indra/llrender/llglheaders.h
+++ b/indra/llrender/llglheaders.h
@@ -68,6 +68,10 @@ extern PFNGLUNMAPBUFFERARBPROC		glUnmapBufferARB;
 extern PFNGLGETBUFFERPARAMETERIVARBPROC	glGetBufferParameterivARB;
 extern PFNGLGETBUFFERPOINTERVARBPROC	glGetBufferPointervARB;
 
+// GL_ARB_map_buffer_range
+extern PFNGLMAPBUFFERRANGEPROC			glMapBufferRange;
+extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC	glFlushMappedBufferRange;
+
 // GL_ATI_vertex_array_object
 extern PFNGLNEWOBJECTBUFFERATIPROC			glNewObjectBufferATI;
 extern PFNGLISOBJECTBUFFERATIPROC			glIsObjectBufferATI;
@@ -306,6 +310,10 @@ extern PFNGLUNMAPBUFFERARBPROC		glUnmapBufferARB;
 extern PFNGLGETBUFFERPARAMETERIVARBPROC	glGetBufferParameterivARB;
 extern PFNGLGETBUFFERPOINTERVARBPROC	glGetBufferPointervARB;
 
+// GL_ARB_map_buffer_range
+extern PFNGLMAPBUFFERRANGEPROC			glMapBufferRange;
+extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC	glFlushMappedBufferRange;
+
 // GL_ATI_vertex_array_object
 extern PFNGLNEWOBJECTBUFFERATIPROC			glNewObjectBufferATI;
 extern PFNGLISOBJECTBUFFERATIPROC			glIsObjectBufferATI;
@@ -511,6 +519,10 @@ extern PFNGLUNMAPBUFFERARBPROC		glUnmapBufferARB;
 extern PFNGLGETBUFFERPARAMETERIVARBPROC	glGetBufferParameterivARB;
 extern PFNGLGETBUFFERPOINTERVARBPROC	glGetBufferPointervARB;
 
+// GL_ARB_map_buffer_range
+extern PFNGLMAPBUFFERRANGEPROC			glMapBufferRange;
+extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC	glFlushMappedBufferRange;
+
 // GL_ATI_vertex_array_object
 extern PFNGLNEWOBJECTBUFFERATIPROC			glNewObjectBufferATI;
 extern PFNGLISOBJECTBUFFERATIPROC			glIsObjectBufferATI;
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 2dab7578284ad3f9772657a09570e01ecaf1478e..5473f23d8679c86cf63fe55c30280b9d7359cb5f 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -48,6 +48,8 @@ using std::pair;
 using std::make_pair;
 using std::string;
 
+U32 LLGLSLShader::sCurBoundShader = 0;
+
 BOOL shouldChange(const LLVector4& v1, const LLVector4& v2)
 {
 	return v1 != v2;
@@ -367,7 +369,7 @@ void LLGLSLShader::bind()
 	if (gGLManager.mHasShaderObjects)
 	{
 		glUseProgramObjectARB(mProgramObject);
-
+		sCurBoundShader = mProgramObject;
 		if (mUniformsDirty)
 		{
 			LLShaderMgr::instance()->updateShaderUniforms(this);
@@ -390,6 +392,7 @@ void LLGLSLShader::unbind()
 			}
 		}
 		glUseProgramObjectARB(0);
+		sCurBoundShader = 0;
 		stop_glerror();
 	}
 }
@@ -397,6 +400,7 @@ void LLGLSLShader::unbind()
 void LLGLSLShader::bindNoShader(void)
 {
 	glUseProgramObjectARB(0);
+	sCurBoundShader = 0;
 }
 
 S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode)
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 51be56b2953d121905623a82451c141fe567ee92..4922eb6d6725bd0eee38177a2d776231c9e27ed1 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -66,6 +66,8 @@ class LLGLSLShader
 	
 	LLGLSLShader();
 
+	static GLhandleARB sCurBoundShader;
+
 	void unload();
 	BOOL createShader(std::vector<std::string> * attributes,
 						std::vector<std::string> * uniforms);
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 3a4139bace90b58fbd600471570d971949b0443f..60a5962234054aa34b64ea9d543759f54113beb6 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1418,11 +1418,13 @@ void LLImageGL::deleteDeadTextures()
 	{
 		GLuint tex = sDeadTextureList.front();
 		sDeadTextureList.pop_front();
-		for (int i = 0; i < gGLManager.mNumTextureUnits; i++)
+		for (int i = 0; i < gGLManager.mNumTextureImageUnits; i++)
 		{
-			if (sCurrentBoundTextures[i] == tex)
+			LLTexUnit* tex_unit = gGL.getTexUnit(i);
+
+			if (tex_unit->getCurrTexture() == tex)
 			{
-				gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType());
+				tex_unit->unbind(tex_unit->getCurrType());
 				stop_glerror();
 			}
 		}
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index fdfcfe7fab14e601b100a923b8c8a8cc346fe572..e91ceb873ee2eadafdc1a9de7c918eadc5cc564f 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -30,6 +30,7 @@
 
 #include "llvertexbuffer.h"
 #include "llcubemap.h"
+#include "llglslshader.h"
 #include "llimagegl.h"
 #include "llrendertarget.h"
 #include "lltexture.h"
@@ -183,7 +184,8 @@ void LLTexUnit::enable(eTextureType type)
 		mCurrTexType = type;
 
 		gGL.flush();
-		if (type != LLTexUnit::TT_MULTISAMPLE_TEXTURE &&
+		if (LLGLSLShader::sCurBoundShader == 0 &&
+			type != LLTexUnit::TT_MULTISAMPLE_TEXTURE &&
 			mIndex < gGLManager.mNumTextureUnits)
 		{
 			glEnable(sGLTextureType[type]);
@@ -200,12 +202,10 @@ void LLTexUnit::disable(void)
 		activate();
 		unbind(mCurrTexType);
 		gGL.flush();
-		if (mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE &&
+		if (LLGLSLShader::sCurBoundShader == 0 && mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE &&
 			mIndex < gGLManager.mNumTextureUnits)
 		{
-			stop_glerror();
 			glDisable(sGLTextureType[mCurrTexType]);
-			stop_glerror();
 		}
 		
 		mCurrTexType = TT_NONE;
@@ -295,7 +295,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind)
 		glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture);
 		texture->updateBindStats(texture->mTextureMemory);		
 		mHasMipMaps = texture->mHasMipMaps;
-		if (texture->mTexOptionsDirty)
+		if (mIndex == 0 && texture->mTexOptionsDirty)
 		{
 			texture->mTexOptionsDirty = false;
 			setTextureAddressMode(texture->mAddressMode);
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index f715a8e9ba76e78bc8863bdfc62eddedf289f33e..27cc88462ad594c1737d21669bec2304d62cb834 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -934,8 +934,26 @@ void LLVertexBuffer::allocateClientIndexBuffer()
 	}
 }
 
+bool expand_region(LLVertexBuffer::MappedRegion& region, S32 index, S32 count)
+{
+	S32 end = index+count;
+	S32 region_end = region.mIndex+region.mCount;
+	
+	if (end < region.mIndex ||
+		index > region_end)
+	{ //gap exists, do not merge
+		return false;
+	}
+
+	S32 new_end = llmax(end, region_end);
+	S32 new_index = llmin(index, region.mIndex);
+	region.mIndex = new_index;
+	region.mCount = new_end-new_index;
+	return true;
+}
+
 // Map for data access
-U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access)
+U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
 	if (mFinal)
@@ -947,8 +965,44 @@ U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access)
 		llerrs << "LLVertexBuffer::mapVertexBuffer() called on unallocated buffer." << llendl;
 	}
 		
-	if (!mVertexLocked && useVBOs())
+	if (useVBOs())
 	{
+
+		if (!sDisableVBOMapping && gGLManager.mHasMapBufferRange)
+		{
+			if (count == -1)
+			{
+				count = mNumVerts;
+			}
+
+			bool mapped = false;
+			//see if range is already mapped
+			for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
+			{
+				MappedRegion& region = mMappedVertexRegions[i];
+				if (region.mType == type)
+				{
+					if (expand_region(region, index, count))
+					{
+						mapped = true;
+					}
+				}
+			}
+
+			if (!mapped)
+			{
+				//not already mapped, map new region
+				MappedRegion region(type, map_range ? -1 : index, count);
+				mMappedVertexRegions.push_back(region);
+			}
+		}
+
+		if (mVertexLocked && map_range)
+		{
+			llerrs << "Attempted to map a specific range of a buffer that was already mapped." << llendl;
+		}
+
+		if (!mVertexLocked)
 		{
 			LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES);
 			setBuffer(0, type);
@@ -957,61 +1011,91 @@ U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access)
 
 			if(sDisableVBOMapping)
 			{
+				map_range = false;
 				allocateClientVertexBuffer() ;
 			}
 			else
 			{
-				U8* src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				U8* src = NULL;
+				if (gGLManager.mHasMapBufferRange)
+				{
+					if (map_range)
+					{
+						S32 offset = mOffsets[type] + sTypeSize[type]*index;
+						S32 length = (sTypeSize[type]*count+0xF) & ~0xF;
+						src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, offset, length, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
+					}
+					else
+					{
+						src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, 0, mSize, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
+					}
+				}
+				else
+				{
+					map_range = false;
+					src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				}
+
 				mMappedData = LL_NEXT_ALIGNED_ADDRESS<U8>(src);
 				mAlignedOffset = mMappedData - src;
 			
 				stop_glerror();
 			}
-		}
-		
-		
-		if (!mMappedData)
-		{
-			log_glerror();
-
-			//check the availability of memory
-			U32 avail_phy_mem, avail_vir_mem;
-			LLMemoryInfo::getAvailableMemoryKB(avail_phy_mem, avail_vir_mem) ;
-			llinfos << "Available physical mwmory(KB): " << avail_phy_mem << llendl ; 
-			llinfos << "Available virtual memory(KB): " << avail_vir_mem << llendl;
-
-			if(!sDisableVBOMapping)
-			{			
-				//--------------------
-				//print out more debug info before crash
-				llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ;
-				GLint size ;
-				glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ;
-				llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ;
-				//--------------------
+				
+			if (!mMappedData)
+			{
+				log_glerror();
+
+				//check the availability of memory
+				U32 avail_phy_mem, avail_vir_mem;
+				LLMemoryInfo::getAvailableMemoryKB(avail_phy_mem, avail_vir_mem) ;
+				llinfos << "Available physical mwmory(KB): " << avail_phy_mem << llendl ; 
+				llinfos << "Available virtual memory(KB): " << avail_vir_mem << llendl;
+
+				if(!sDisableVBOMapping)
+				{			
+					//--------------------
+					//print out more debug info before crash
+					llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ;
+					GLint size ;
+					glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ;
+					llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ;
+					//--------------------
+
+					GLint buff;
+					glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
+					if ((GLuint)buff != mGLBuffer)
+					{
+						llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
+					}
 
-				GLint buff;
-				glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
-				if ((GLuint)buff != mGLBuffer)
+							
+					llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl;
+				}
+				else
 				{
-					llerrs << "Invalid GL vertex buffer bound: " << buff << llendl;
+					llerrs << "memory allocation for vertex data failed." << llendl ;
 				}
-
-							
-				llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl;
-			}
-			else
-			{
-				llerrs << "memory allocation for vertex data failed." << llendl ;
 			}
+			sMappedCount++;
 		}
-		sMappedCount++;
+	}
+	else
+	{
+		map_range = false;
 	}
 	
-	return mMappedData;
+	if (map_range)
+	{
+		return mMappedData;
+	}
+	else
+	{
+		return mMappedData+mOffsets[type]+sTypeSize[type]*index;
+	}
 }
 
-U8* LLVertexBuffer::mapIndexBuffer(S32 access)
+U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
 	if (mFinal)
@@ -1023,8 +1107,40 @@ U8* LLVertexBuffer::mapIndexBuffer(S32 access)
 		llerrs << "LLVertexBuffer::mapIndexBuffer() called on unallocated buffer." << llendl;
 	}
 
-	if (!mIndexLocked && useVBOs())
+	if (useVBOs())
 	{
+		if (!sDisableVBOMapping && gGLManager.mHasMapBufferRange)
+		{
+			if (count == -1)
+			{
+				count = mNumIndices;
+			}
+
+			bool mapped = false;
+			//see if range is already mapped
+			for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
+			{
+				MappedRegion& region = mMappedIndexRegions[i];
+				if (expand_region(region, index, count))
+				{
+					mapped = true;
+				}
+			}
+
+			if (!mapped)
+			{
+				//not already mapped, map new region
+				MappedRegion region(TYPE_INDEX, map_range ? -1 : index, count);
+				mMappedIndexRegions.push_back(region);
+			}
+		}
+
+		if (mIndexLocked && map_range)
+		{
+			llerrs << "Attempted to map a specific range of a buffer that was already mapped." << llendl;
+		}
+
+		if (!mIndexLocked)
 		{
 			LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
 
@@ -1034,12 +1150,32 @@ U8* LLVertexBuffer::mapIndexBuffer(S32 access)
 
 			if(sDisableVBOMapping)
 			{
+				map_range = false;
 				allocateClientIndexBuffer() ;
 			}
 			else
 			{
-				U8* src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
-				mMappedIndexData = LL_NEXT_ALIGNED_ADDRESS<U8>(src);
+				U8* src = NULL;
+				if (gGLManager.mHasMapBufferRange)
+				{
+					if (map_range)
+					{
+						S32 offset = sizeof(U16)*index;
+						S32 length = sizeof(U16)*count;
+						src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
+					}
+					else
+					{
+						src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, sizeof(U16)*mNumIndices, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
+					}
+				}
+				else
+				{
+					map_range = false;
+					src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+				}
+
+				mMappedIndexData = src; //LL_NEXT_ALIGNED_ADDRESS<U8>(src);
 				mAlignedIndexOffset = mMappedIndexData - src;
 				stop_glerror();
 			}
@@ -1068,19 +1204,31 @@ U8* LLVertexBuffer::mapIndexBuffer(S32 access)
 
 		sMappedCount++;
 	}
+	else
+	{
+		map_range = false;
+	}
 
-	return mMappedIndexData ;
+	if (map_range)
+	{
+		return mMappedIndexData;
+	}
+	else
+	{
+		return mMappedIndexData + sizeof(U16)*index;
+	}
 }
 
 void LLVertexBuffer::unmapBuffer(S32 type)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
-	if (!useVBOs())
+	if (!useVBOs() || type == -2)
 	{
 		return ; //nothing to unmap
 	}
 
 	bool updated_all = false ;
+
 	if (mMappedData && mVertexLocked && type != TYPE_INDEX)
 	{
 		updated_all = (mIndexLocked && type < 0) ; //both vertex and index buffers done updating
@@ -1093,6 +1241,23 @@ void LLVertexBuffer::unmapBuffer(S32 type)
 		}
 		else
 		{
+			if (gGLManager.mHasMapBufferRange)
+			{
+				if (!mMappedVertexRegions.empty())
+				{
+					stop_glerror();
+					for (U32 i = 0; i < mMappedVertexRegions.size(); ++i)
+					{
+						const MappedRegion& region = mMappedVertexRegions[i];
+						S32 offset = region.mIndex >= 0 ? mOffsets[region.mType]+sTypeSize[region.mType]*region.mIndex : 0;
+						S32 length = sTypeSize[region.mType]*region.mCount;
+						glFlushMappedBufferRange(GL_ARRAY_BUFFER_ARB, offset, length);
+						stop_glerror();
+					}
+
+					mMappedVertexRegions.clear();
+				}
+			}
 			stop_glerror();
 			glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
 			stop_glerror();
@@ -1103,8 +1268,8 @@ void LLVertexBuffer::unmapBuffer(S32 type)
 		mVertexLocked = FALSE ;
 		sMappedCount--;
 	}
-
-	if(mMappedIndexData && mIndexLocked && (type < 0 || type == TYPE_INDEX))
+	
+	if (mMappedIndexData && mIndexLocked && (type < 0 || type == TYPE_INDEX))
 	{
 		if(sDisableVBOMapping)
 		{
@@ -1114,6 +1279,23 @@ void LLVertexBuffer::unmapBuffer(S32 type)
 		}
 		else
 		{
+
+			if (gGLManager.mHasMapBufferRange)
+			{
+				if (!mMappedIndexRegions.empty())
+				{
+					for (U32 i = 0; i < mMappedIndexRegions.size(); ++i)
+					{
+						const MappedRegion& region = mMappedIndexRegions[i];
+						S32 offset = region.mIndex >= 0 ? sizeof(U16)*region.mIndex : 0;
+						S32 length = sizeof(U16)*region.mCount;
+						glFlushMappedBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length);
+						stop_glerror();
+					}
+
+					mMappedIndexRegions.clear();
+				}
+			}
 			stop_glerror();
 			glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
 			stop_glerror();
@@ -1152,19 +1334,21 @@ template <class T,S32 type> struct VertexBufferStrider
 	typedef LLStrider<T> strider_t;
 	static bool get(LLVertexBuffer& vbo, 
 					strider_t& strider, 
-					S32 index)
+					S32 index, S32 count, bool map_range)
 	{
 		if (type == LLVertexBuffer::TYPE_INDEX)
 		{
 			S32 stride = sizeof(T);
 
-			if (vbo.mapIndexBuffer() == NULL)
+			U8* ptr = vbo.mapIndexBuffer(index, count, map_range);
+
+			if (ptr == NULL)
 			{
 				llwarns << "mapIndexBuffer failed!" << llendl;
 				return FALSE;
 			}
 
-			strider = (T*)(vbo.getMappedIndices() + index*stride);
+			strider = (T*)ptr;
 			strider.setStride(0);
 			return TRUE;
 		}
@@ -1172,13 +1356,15 @@ template <class T,S32 type> struct VertexBufferStrider
 		{
 			S32 stride = LLVertexBuffer::sTypeSize[type];
 
-			if (vbo.mapVertexBuffer(type) == NULL)
+			U8* ptr = vbo.mapVertexBuffer(type, index, count, map_range);
+
+			if (ptr == NULL)
 			{
 				llwarns << "mapVertexBuffer failed!" << llendl;
 				return FALSE;
 			}
 
-			strider = (T*)(vbo.getMappedData() + vbo.getOffset(type)+index*stride);
+			strider = (T*)ptr;
 			strider.setStride(stride);
 			return TRUE;
 		}
@@ -1190,55 +1376,48 @@ template <class T,S32 type> struct VertexBufferStrider
 	}
 };
 
-bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector3>& strider, S32 index)
+bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector3,TYPE_VERTEX>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector3,TYPE_VERTEX>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, S32 index)
+bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index);
+	return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index)
+bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD0>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD0>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index)
+bool LLVertexBuffer::getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD1>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD1>::get(*this, strider, index, count, map_range);
 }
-/*bool LLVertexBuffer::getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index)
-{
-	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD2>::get(*this, strider, index);
-}
-bool LLVertexBuffer::getTexCoord3Strider(LLStrider<LLVector2>& strider, S32 index)
-{
-	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD3>::get(*this, strider, index);
-}*/
-bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index)
+
+bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getBinormalStrider(LLStrider<LLVector3>& strider, S32 index)
+bool LLVertexBuffer::getBinormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector3,TYPE_BINORMAL>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector3,TYPE_BINORMAL>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getColorStrider(LLStrider<LLColor4U>& strider, S32 index)
+bool LLVertexBuffer::getColorStrider(LLStrider<LLColor4U>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLColor4U,TYPE_COLOR>::get(*this, strider, index);
+	return VertexBufferStrider<LLColor4U,TYPE_COLOR>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getWeightStrider(LLStrider<F32>& strider, S32 index)
+bool LLVertexBuffer::getWeightStrider(LLStrider<F32>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<F32,TYPE_WEIGHT>::get(*this, strider, index);
+	return VertexBufferStrider<F32,TYPE_WEIGHT>::get(*this, strider, index, count, map_range);
 }
 
-bool LLVertexBuffer::getWeight4Strider(LLStrider<LLVector4>& strider, S32 index)
+bool LLVertexBuffer::getWeight4Strider(LLStrider<LLVector4>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector4,TYPE_WEIGHT4>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector4,TYPE_WEIGHT4>::get(*this, strider, index, count, map_range);
 }
 
-bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index)
+bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index);
+	return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index, count, map_range);
 }
 
 //----------------------------------------------------------------------------
@@ -1510,11 +1689,3 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const
 	llglassertok();
 }
 
-void LLVertexBuffer::markDirty(U32 vert_index, U32 vert_count, U32 indices_index, U32 indices_count)
-{
-	// TODO: use GL_APPLE_flush_buffer_range here
-	/*if (useVBOs() && !mFilthy)
-	{
-	
-	}*/
-}
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index 0c4b2415373fa35af62ea05d201a29872eeec643..aa5df305a66f0eda5bf8b31caa304311f454e4d1 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -77,6 +77,18 @@ class LLVBOPool : public LLGLNamePool
 class LLVertexBuffer : public LLRefCount
 {
 public:
+	class MappedRegion
+	{
+	public:
+		S32 mType;
+		S32 mIndex;
+		S32 mCount;
+		
+		MappedRegion(S32 type, S32 index, S32 count)
+			: mType(type), mIndex(index), mCount(count)
+		{ }	
+	};
+
 	LLVertexBuffer(const LLVertexBuffer& rhs)
 	{
 		*this = rhs;
@@ -177,8 +189,8 @@ class LLVertexBuffer : public LLRefCount
 	LLVertexBuffer(U32 typemask, S32 usage);
 	
 	// map for data access
-	U8*		mapVertexBuffer(S32 type = -1, S32 access = -1);
-	U8*		mapIndexBuffer(S32 access = -1);
+	U8*		mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range);
+	U8*		mapIndexBuffer(S32 index, S32 count, bool map_range);
 
 	// set for rendering
 	virtual void	setBuffer(U32 data_mask, S32 type = -1); 	// calls  setupVertexBuffer() if data_mask is not 0
@@ -193,16 +205,16 @@ class LLVertexBuffer : public LLRefCount
 	//   vb->getNormalStrider(norms);
 	//   setVertsNorms(verts, norms);
 	//   vb->unmapBuffer();
-	bool getVertexStrider(LLStrider<LLVector3>& strider, S32 index=0);
-	bool getIndexStrider(LLStrider<U16>& strider, S32 index=0);
-	bool getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index=0);
-	bool getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index=0);
-	bool getNormalStrider(LLStrider<LLVector3>& strider, S32 index=0);
-	bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0);
-	bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0);
-	bool getWeightStrider(LLStrider<F32>& strider, S32 index=0);
-	bool getWeight4Strider(LLStrider<LLVector4>& strider, S32 index=0);
-	bool getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index=0);
+	bool getVertexStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getIndexStrider(LLStrider<U16>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getNormalStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getWeightStrider(LLStrider<F32>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getWeight4Strider(LLStrider<LLVector4>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	
 	BOOL isEmpty() const					{ return mEmpty; }
 	BOOL isLocked() const					{ return mVertexLocked || mIndexLocked; }
@@ -222,8 +234,6 @@ class LLVertexBuffer : public LLRefCount
 	S32 getOffset(S32 type) const			{ return mOffsets[type]; }
 	S32 getUsage() const					{ return mUsage; }
 
-	void markDirty(U32 vert_index, U32 vert_count, U32 indices_index, U32 indices_count);
-
 	void draw(U32 mode, U32 count, U32 indices_offset) const;
 	void drawArrays(U32 mode, U32 offset, U32 count) const;
 	void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;
@@ -257,20 +267,8 @@ class LLVertexBuffer : public LLRefCount
 	BOOL	mDynamicSize;	// if TRUE, buffer has been resized at least once (and should be padded)
 	S32		mOffsets[TYPE_MAX];
 
-	class DirtyRegion
-	{
-	public:
-		U32 mIndex;
-		U32 mCount;
-		U32 mIndicesIndex;
-		U32 mIndicesCount;
-
-		DirtyRegion(U32 vi, U32 vc, U32 ii, U32 ic)
-			: mIndex(vi), mCount(vc), mIndicesIndex(ii), mIndicesCount(ic)
-		{ }	
-	};
-
-	std::vector<DirtyRegion> mDirtyRegions; //vector of dirty regions to rebuild
+	std::vector<MappedRegion> mMappedVertexRegions;
+	std::vector<MappedRegion> mMappedIndexRegions;
 
 public:
 	static S32 sCount;
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index bed7e4705efa5121f64ed3c449f830c53691d308..b7ba9e3607937be8831a0e0c1d7c0022f7d378d6 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7126,7 +7126,7 @@
     <string>Vector3</string>
     <key>Value</key>
     <array>
-      <real>0.05</real>
+      <real>0.1</real>
       <real>0.0</real>
       <real>0.0</real>
     </array>
@@ -7154,7 +7154,7 @@
     <string>Vector3</string>
     <key>Value</key>
     <array>
-      <real>0.25</real>
+      <real>0.01</real>
       <real>0.0</real>
       <real>0.0</real>
     </array>
@@ -7513,6 +7513,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+  <key>RenderMaxTextureIndex</key>
+  <map>
+    <key>Comment</key>
+    <string>Maximum texture index to use for indexed texture rendering.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>6</integer>
+  </map>
     <key>RenderDebugTextureBind</key>
     <map>
       <key>Comment</key>
@@ -9011,7 +9022,7 @@
       <key>Type</key>
       <string>Boolean</string>
       <key>Value</key>
-      <integer>1</integer>
+      <integer>0</integer>
     </map>
   <key>RenderUseStreamVBO</key>
   <map>
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index d92d9e92a1e29e9908fdb08e2ccb283589a25041..f5483d969d196ce1095d2c54da32a4a6e326ac9e 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -191,7 +191,7 @@ void LLDrawPool::renderPostDeferred(S32 pass)
 //virtual
 void LLDrawPool::endRenderPass( S32 pass )
 {
-	for (U32 i = 1; i < gGLManager.mNumTextureImageUnits; i++)
+	for (U32 i = 0; i < gGLManager.mNumTextureImageUnits; i++)
 	{ //dummy cleanup of any currently bound textures
 		if (gGL.getTexUnit(i)->getCurrType() != LLTexUnit::TT_NONE)
 		{
diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp
index 6963fea26b977243a61888ad1a60b80af423d4c3..5dbb27cabb84e174b0dcb0b9a7f4c82daa266ec6 100644
--- a/indra/newview/lldrawpoolsimple.cpp
+++ b/indra/newview/lldrawpoolsimple.cpp
@@ -71,14 +71,7 @@ void LLDrawPoolGlow::renderPostDeferred(S32 pass)
 void LLDrawPoolGlow::endPostDeferredPass(S32 pass)
 {
 	gDeferredFullbrightProgram.unbind();
-	for (U32 i = 0; i < 8; i++)
-	{
-		if (gGL.getTexUnit(i)->getCurrType() != LLTexUnit::TT_NONE)
-		{
-			gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType());
-			gGL.getTexUnit(i)->disable();
-		}
-	}
+	LLRenderPass::endRenderPass(pass);
 }
 
 void LLDrawPoolGlow::render(S32 pass)
@@ -225,15 +218,6 @@ void LLDrawPoolSimple::endDeferredPass(S32 pass)
 	LLRenderPass::endRenderPass(pass);
 
 	gDeferredDiffuseProgram.unbind();
-
-	for (U32 i = 0; i < 8; i++)
-	{
-		if (gGL.getTexUnit(i)->getCurrType() != LLTexUnit::TT_NONE)
-		{
-			gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType());
-			gGL.getTexUnit(i)->disable();
-		}
-	}
 }
 
 void LLDrawPoolSimple::renderDeferred(S32 pass)
@@ -368,14 +352,7 @@ void LLDrawPoolFullbright::renderPostDeferred(S32 pass)
 void LLDrawPoolFullbright::endPostDeferredPass(S32 pass)
 {
 	gDeferredFullbrightProgram.unbind();
-	for (U32 i = 0; i < 8; i++)
-	{
-		if (gGL.getTexUnit(i)->getCurrType() != LLTexUnit::TT_NONE)
-		{
-			gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType());
-			gGL.getTexUnit(i)->disable();
-		}
-	}
+	LLRenderPass::endRenderPass(pass);
 }
 
 void LLDrawPoolFullbright::beginRenderPass(S32 pass)
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index e30522d380f601ac060c7883ee858453088e3c95..540ed054e9ffccfb2999fa1184866f0271362df7 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -429,11 +429,11 @@ U16 LLFace::getGeometryAvatar(
 
 	if (mVertexBuffer.notNull())
 	{
-		mVertexBuffer->getVertexStrider      (vertices, mGeomIndex);
-		mVertexBuffer->getNormalStrider      (normals, mGeomIndex);
-		mVertexBuffer->getTexCoord0Strider    (tex_coords, mGeomIndex);
-		mVertexBuffer->getWeightStrider(vertex_weights, mGeomIndex);
-		mVertexBuffer->getClothWeightStrider(clothing_weights, mGeomIndex);
+		mVertexBuffer->getVertexStrider      (vertices, mGeomIndex, mGeomCount);
+		mVertexBuffer->getNormalStrider      (normals, mGeomIndex, mGeomCount);
+		mVertexBuffer->getTexCoord0Strider    (tex_coords, mGeomIndex, mGeomCount);
+		mVertexBuffer->getWeightStrider(vertex_weights, mGeomIndex, mGeomCount);
+		mVertexBuffer->getClothWeightStrider(clothing_weights, mGeomIndex, mGeomCount);
 	}
 
 	return mGeomIndex;
@@ -446,17 +446,17 @@ U16 LLFace::getGeometry(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &no
 	
 	if (mVertexBuffer.notNull())
 	{
-		mVertexBuffer->getVertexStrider(vertices,   mGeomIndex);
+		mVertexBuffer->getVertexStrider(vertices,   mGeomIndex, mGeomCount);
 		if (mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL))
 		{
-			mVertexBuffer->getNormalStrider(normals,    mGeomIndex);
+			mVertexBuffer->getNormalStrider(normals,    mGeomIndex, mGeomCount);
 		}
 		if (mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD0))
 		{
-			mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex);
+			mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex, mGeomCount);
 		}
 
-		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
+		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex, mIndicesCount);
 	}
 	
 	return mGeomIndex;
@@ -1092,27 +1092,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	const LLTextureEntry *tep = mVObjp->getTE(f);
 	const U8 bump_code = tep ? tep->getBumpmap() : 0;
 
-	if (rebuild_pos)
-	{
-		mVertexBuffer->getVertexStrider(vert, mGeomIndex);
-		vertices = (LLVector4a*) vert.get();
-	}
-	if (rebuild_normal)
-	{
-		mVertexBuffer->getNormalStrider(norm, mGeomIndex);
-		normals = (LLVector4a*) norm.get();
-	}
-	if (rebuild_binormal)
-	{
-		mVertexBuffer->getBinormalStrider(binorm, mGeomIndex);
-		binormals = (LLVector4a*) binorm.get();
-	}
-	if (rebuild_weights)
-	{
-		mVertexBuffer->getWeight4Strider(wght, mGeomIndex);
-		weights = (LLVector4a*) wght.get();
-	}
-	
 	F32 tcoord_xoffset = 0.f ;
 	F32 tcoord_yoffset = 0.f ;
 	F32 tcoord_xscale = 1.f ;
@@ -1121,12 +1100,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 
 	if (rebuild_tcoord)
 	{
-		mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex);
-		if (bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1))
-		{
-			mVertexBuffer->getTexCoord1Strider(tex_coords2, mGeomIndex);
-		}
-
 		in_atlas = isAtlasInUse() ;
 		if(in_atlas)
 		{
@@ -1139,11 +1112,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			tcoord_yscale = tmp->mV[1] ;	
 		}
 	}
-	if (rebuild_color)
-	{	
-		mVertexBuffer->getColorStrider(colors, mGeomIndex);
-	}
-
+	
 	BOOL is_static = mDrawablep->isStatic();
 	BOOL is_global = is_static;
 
@@ -1182,7 +1151,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	// INDICES
 	if (full_rebuild)
 	{
-		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
+		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex, mIndicesCount, true);
+
 		__m128i* dst = (__m128i*) indicesp.get();
 		__m128i* src = (__m128i*) vf.mIndices;
 		__m128i offset = _mm_set1_epi16(index_offset);
@@ -1199,6 +1169,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		{
 			indicesp[i] = vf.mIndices[i]+index_offset;
 		}
+
+		mVertexBuffer->setBuffer(0);
 	}
 	
 	LLMatrix4a mat_normal;
@@ -1344,6 +1316,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 
 		if (!in_atlas && !do_bump)
 		{ //not in atlas or not bump mapped, might be able to do a cheap update
+			mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex, mGeomCount);
+
 			if (texgen != LLTextureEntry::TEX_GEN_PLANAR)
 			{
 				if (!do_tex_mat)
@@ -1416,9 +1390,15 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 					}
 				}
 			}
+
+			mVertexBuffer->setBuffer(0);
 		}
 		else
 		{ //either bump mapped or in atlas, just do the whole expensive loop
+			mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex, mGeomCount, true);
+
+			std::vector<LLVector2> bump_tc;
+		
 			for (S32 i = 0; i < num_vertices; i++)
 			{	
 				LLVector2 tc(vf.mTexCoords[i]);
@@ -1549,8 +1529,20 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 				
 
 				*tex_coords++ = tc;
-				
-				if (bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1))
+				if (do_bump)
+				{
+					bump_tc.push_back(tc);
+				}
+			}
+
+			mVertexBuffer->setBuffer(0);
+
+
+			if (do_bump)
+			{
+				mVertexBuffer->getTexCoord1Strider(tex_coords2, mGeomIndex, mGeomCount, true);
+		
+				for (S32 i = 0; i < num_vertices; i++)
 				{
 					LLVector4a tangent;
 					tangent.setCross3(vf.mBinormals[i], vf.mNormals[i]);
@@ -1572,16 +1564,22 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 					}
 
 					binormal.normalize3fast();
+					LLVector2 tc = bump_tc[i];
 					tc += LLVector2( bump_s_primary_light_ray.dot3(tangent).getF32(), bump_t_primary_light_ray.dot3(binormal).getF32() );
 					
 					*tex_coords2++ = tc;
-				}	
+				}
+
+				mVertexBuffer->setBuffer(0);
 			}
 		}
 	}
 
 	if (rebuild_pos)
 	{
+		mVertexBuffer->getVertexStrider(vert, mGeomIndex, mGeomCount, true);
+		vertices = (LLVector4a*) vert.get();
+	
 		LLMatrix4a mat_vert;
 		mat_vert.loadu(mat_vert_in);
 
@@ -1607,11 +1605,15 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			index_dst += 4;
 		}
 		while (index_dst < index_end);
-		
+
+		mVertexBuffer->setBuffer(0);
 	}
 		
 	if (rebuild_normal)
 	{
+		mVertexBuffer->getNormalStrider(norm, mGeomIndex, mGeomCount, true);
+		normals = (LLVector4a*) norm.get();
+	
 		for (S32 i = 0; i < num_vertices; i++)
 		{	
 			LLVector4a normal;
@@ -1619,10 +1621,15 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			normal.normalize3fast();
 			normals[i] = normal;
 		}
+
+		mVertexBuffer->setBuffer(0);
 	}
 		
 	if (rebuild_binormal)
 	{
+		mVertexBuffer->getBinormalStrider(binorm, mGeomIndex, mGeomCount, true);
+		binormals = (LLVector4a*) binorm.get();
+		
 		for (S32 i = 0; i < num_vertices; i++)
 		{	
 			LLVector4a binormal;
@@ -1630,15 +1637,22 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			binormal.normalize3fast();
 			binormals[i] = binormal;
 		}
+
+		mVertexBuffer->setBuffer(0);
 	}
 	
 	if (rebuild_weights && vf.mWeights)
 	{
+		mVertexBuffer->getWeight4Strider(wght, mGeomIndex, mGeomCount, true);
+		weights = (LLVector4a*) wght.get();
 		LLVector4a::memcpyNonAliased16((F32*) weights, (F32*) vf.mWeights, num_vertices*4*sizeof(F32));
+		mVertexBuffer->setBuffer(0);
 	}
 
 	if (rebuild_color)
 	{
+		mVertexBuffer->getColorStrider(colors, mGeomIndex, mGeomCount, true);
+
 		LLVector4a src;
 
 		U32 vec[4];
@@ -1657,6 +1671,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		{	
 			dst[i] = src;
 		}
+
+		mVertexBuffer->setBuffer(0);
 	}
 
 	if (rebuild_tcoord)
@@ -2073,13 +2089,13 @@ S32 LLFace::getColors(LLStrider<LLColor4U> &colors)
 	}
 	
 	// llassert(mGeomIndex >= 0);
-	mVertexBuffer->getColorStrider(colors, mGeomIndex);
+	mVertexBuffer->getColorStrider(colors, mGeomIndex, mGeomCount);
 	return mGeomIndex;
 }
 
 S32	LLFace::getIndices(LLStrider<U16> &indicesp)
 {
-	mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
+	mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex, mIndicesCount);
 	llassert(indicesp[0] != indicesp[1]);
 	return mIndicesIndex;
 }
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index ae5d4fa463f72dbc461fb72155f0e2f29f13fa75..db8a0c29926a6b7d4356b5813c7c48c163175766 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -209,7 +209,7 @@ class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 	typedef std::vector<LLPointer<LLDrawInfo> > drawmap_elem_t; 
 	typedef std::map<U32, drawmap_elem_t > draw_map_t;	
 	typedef std::vector<LLPointer<LLVertexBuffer> > buffer_list_t;
-	typedef std::map<LLPointer<LLViewerTexture>, buffer_list_t> buffer_texture_map_t;
+	typedef std::map<LLFace*, buffer_list_t> buffer_texture_map_t;
 	typedef std::map<U32, buffer_texture_map_t> buffer_map_t;
 
 	typedef LLOctreeListener<LLDrawable>	BaseType;
@@ -401,7 +401,7 @@ class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 
 public:
 	bridge_list_t mBridgeList;
-	buffer_map_t mBufferMap; //used by volume buffers to store unique buffers per texture
+	buffer_map_t mBufferMap; //used by volume buffers to attempt to reuse vertex buffers
 
 	F32 mBuilt;
 	OctreeNode* mOctreeNode;
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 61200db2b966ec7fff653043f4eba424fa48ee8f..87ca80260f4337a52e6dc54bceb31c2708651525 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -591,6 +591,7 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("OctreeMaxNodeCapacity")->getSignal()->connect(boost::bind(&handleRepartition, _2));
 	gSavedSettings.getControl("OctreeAlphaDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
 	gSavedSettings.getControl("OctreeAttachmentSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
+	gSavedSettings.getControl("RenderMaxTextureIndex")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderUseTriStrips")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderAnimateTrees")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
 	gSavedSettings.getControl("RenderAvatarVP")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 125283d474d00da0cdcf5e94346f08b368182da4..58e38ea9b1bf6a127ca90b454da2cbefc59106a4 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -4426,6 +4426,33 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
 	llassert(!group || !group->isState(LLSpatialGroup::NEW_DRAWINFO));
 }
 
+struct CompareBatchBreakerModified
+{
+	bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+	{
+		const LLTextureEntry* lte = lhs->getTextureEntry();
+		const LLTextureEntry* rte = rhs->getTextureEntry();
+
+		if (lte->getBumpmap() != rte->getBumpmap())
+		{
+			return lte->getBumpmap() < rte->getBumpmap();
+		}
+		else if (lte->getFullbright() != rte->getFullbright())
+		{
+			return lte->getFullbright() < rte->getFullbright();
+		}
+		else  if (lte->getGlow() != rte->getGlow())
+		{
+			return lte->getGlow() < rte->getGlow();
+		}
+		else
+		{
+			return lhs->getTexture() < rhs->getTexture();
+		}
+		
+	}
+};
+
 void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector<LLFace*>& faces, BOOL distance_sort, BOOL batch_textures)
 {
 	//calculate maximum number of vertices to store in a single buffer
@@ -4435,7 +4462,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 	if (!distance_sort)
 	{
 		//sort faces by things that break batches
-		std::sort(faces.begin(), faces.end(), LLFace::CompareBatchBreaker());
+		std::sort(faces.begin(), faces.end(), CompareBatchBreakerModified());
 	}
 	else
 	{
@@ -4462,6 +4489,8 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 		texture_index_channels = gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels;
 	}
 
+	texture_index_channels = llmin(texture_index_channels, (S32) gSavedSettings.getU32("RenderMaxTextureIndex"));
+	
 
 	while (face_iter != faces.end())
 	{
@@ -4587,7 +4616,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 	
 		//create/delete/resize vertex buffer if needed
 		LLVertexBuffer* buffer = NULL;
-		LLSpatialGroup::buffer_texture_map_t::iterator found_iter = group->mBufferMap[mask].find(tex);
+		LLSpatialGroup::buffer_texture_map_t::iterator found_iter = group->mBufferMap[mask].find(*face_iter);
 		
 		if (found_iter != group->mBufferMap[mask].end())
 		{
@@ -4618,7 +4647,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 			}
 		}
 
-		buffer_map[mask][tex].push_back(buffer);
+		buffer_map[mask][*face_iter].push_back(buffer);
 
 		//add face geometry
 
@@ -4649,12 +4678,8 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 
 					U32 te_idx = facep->getTEOffset();
 
-					if (facep->getGeometryVolume(*volume, te_idx, 
-						vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset))
-					{
-						buffer->markDirty(facep->getGeomIndex(), facep->getGeomCount(), 
-							facep->getIndicesStart(), facep->getIndicesCount());
-					}
+					facep->getGeometryVolume(*volume, te_idx, 
+						vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset);
 				}
 			}