From 7b6723d1e0158d5dc326266a0332e87f634f9755 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Thu, 13 Oct 2011 01:19:45 -0500
Subject: [PATCH] SH-1650 Mitigate memory fragmentation by holding onto and
 reusing VBOs

---
 indra/llrender/llvertexbuffer.cpp       | 458 ++++++++++--------------
 indra/llrender/llvertexbuffer.h         |  69 ++--
 indra/newview/lldrawpoolavatar.cpp      |   6 +-
 indra/newview/lldrawpooltree.cpp        |   4 +-
 indra/newview/llface.cpp                |  13 +-
 indra/newview/llfloatermodelpreview.cpp |   2 +-
 indra/newview/llspatialpartition.cpp    |  10 +-
 indra/newview/llviewerdisplay.cpp       |   4 -
 indra/newview/llviewerwindow.cpp        |   2 +-
 indra/newview/llvoavatar.cpp            |   4 +-
 indra/newview/llvowlsky.cpp             |   4 +-
 indra/newview/pipeline.cpp              |  26 +-
 12 files changed, 257 insertions(+), 345 deletions(-)

diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 4484a880cc..ad99bd1807 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -38,6 +38,17 @@
 #include "llglslshader.h"
 #include "llmemory.h"
 
+//Next Highest Power Of Two
+//helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
+U32 nhpo2(U32 v)
+{
+	U32 r = 1;
+	while (r < v) {
+		r *= 2;
+	}
+	return r;
+}
+
 
 //============================================================================
 
@@ -46,6 +57,7 @@ LLVBOPool LLVertexBuffer::sStreamVBOPool;
 LLVBOPool LLVertexBuffer::sDynamicVBOPool;
 LLVBOPool LLVertexBuffer::sStreamIBOPool;
 LLVBOPool LLVertexBuffer::sDynamicIBOPool;
+U32 LLVBOPool::sBytesPooled = 0;
 
 LLPrivateMemoryPool* LLVertexBuffer::sPrivatePoolp = NULL ;
 U32 LLVertexBuffer::sBindCount = 0;
@@ -66,7 +78,6 @@ BOOL LLVertexBuffer::sMapped = FALSE;
 BOOL LLVertexBuffer::sUseStreamDraw = TRUE;
 BOOL LLVertexBuffer::sUseVAO = FALSE;
 BOOL LLVertexBuffer::sPreferStreamDraw = FALSE;
-std::vector<U32> LLVertexBuffer::sDeleteList;
 
 const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000;  //1 ms
 
@@ -122,6 +133,107 @@ public:
 
 };
 
+
+//which power of 2 is i?
+//assumes i is a power of 2 > 0
+U32 wpo2(U32 i)
+{
+	llassert(i > 0);
+	llassert(nhpo2(i) == i);
+
+	U32 r = 0;
+
+	while (i >>= 1) ++r;
+
+	return r;
+}
+
+U8* LLVBOPool::allocate(U32& name, U32 size)
+{
+	llassert(nhpo2(size) == size);
+
+	U32 i = wpo2(size);
+
+	if (mFreeList.size() <= i)
+	{
+		mFreeList.resize(i+1);
+	}
+
+	U8* ret = NULL;
+
+	if (mFreeList[i].empty())
+	{
+		//make a new buffer
+		glGenBuffersARB(1, &name);
+		glBindBufferARB(mType, name);
+		glBufferDataARB(mType, size, 0, mUsage);
+		LLVertexBuffer::sAllocatedBytes += size;
+
+		if (LLVertexBuffer::sDisableVBOMapping)
+		{
+			ret = (U8*) ll_aligned_malloc_16(size);
+		}
+		glBindBufferARB(mType, 0);
+	}
+	else
+	{
+		name = mFreeList[i].front().mGLName;
+		ret = mFreeList[i].front().mClientData;
+
+		sBytesPooled -= size;
+
+		mFreeList[i].pop_front();
+	}
+
+	return ret;
+}
+
+void LLVBOPool::release(U32 name, U8* buffer, U32 size)
+{
+	llassert(nhpo2(size) == size);
+
+	U32 i = wpo2(size);
+
+	llassert(mFreeList.size() > i);
+
+	Record rec;
+	rec.mGLName = name;
+	rec.mClientData = buffer;
+
+	sBytesPooled += size;
+
+	mFreeList[i].push_back(rec);
+}
+
+void LLVBOPool::cleanup()
+{
+	U32 size = 1;
+
+	for (U32 i = 0; i < mFreeList.size(); ++i)
+	{
+		record_list_t& l = mFreeList[i];
+
+		while (!l.empty())
+		{
+			Record& r = l.front();
+
+			glDeleteBuffersARB(1, &r.mGLName);
+
+			if (r.mClientData)
+			{
+				ll_aligned_free_16(r.mClientData);
+			}
+
+			l.pop_front();
+
+			LLVertexBuffer::sAllocatedBytes -= size;
+		}
+
+		size *= 2;
+	}
+}
+
+
 //NOTE: each component must be AT LEAST 4 bytes in size to avoid a performance penalty on AMD hardware
 S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
 {
@@ -374,16 +486,16 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto
 
 void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
 {
-	if (start >= (U32) mRequestedNumVerts ||
-	    end >= (U32) mRequestedNumVerts)
+	if (start >= (U32) mNumVerts ||
+	    end >= (U32) mNumVerts)
 	{
-		llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mRequestedNumVerts << llendl;
+		llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << llendl;
 	}
 
-	llassert(mRequestedNumIndices >= 0);
+	llassert(mNumIndices >= 0);
 
-	if (indices_offset >= (U32) mRequestedNumIndices ||
-	    indices_offset + count > (U32) mRequestedNumIndices)
+	if (indices_offset >= (U32) mNumIndices ||
+	    indices_offset + count > (U32) mNumIndices)
 	{
 		llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
 	}
@@ -407,7 +519,7 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi
 
 	gGL.syncMatrices();
 
-	llassert(mRequestedNumVerts >= 0);
+	llassert(mNumVerts >= 0);
 	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
 
 	if (mGLArray)
@@ -462,9 +574,9 @@ void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
 
 	gGL.syncMatrices();
 
-	llassert(mRequestedNumIndices >= 0);
-	if (indices_offset >= (U32) mRequestedNumIndices ||
-	    indices_offset + count > (U32) mRequestedNumIndices)
+	llassert(mNumIndices >= 0);
+	if (indices_offset >= (U32) mNumIndices ||
+	    indices_offset + count > (U32) mNumIndices)
 	{
 		llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
 	}
@@ -508,9 +620,9 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
 	
 	gGL.syncMatrices();
 	
-	llassert(mRequestedNumVerts >= 0);
-	if (first >= (U32) mRequestedNumVerts ||
-	    first + count > (U32) mRequestedNumVerts)
+	llassert(mNumVerts >= 0);
+	if (first >= (U32) mNumVerts ||
+	    first + count > (U32) mNumVerts)
 	{
 		llerrs << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << llendl;
 	}
@@ -546,23 +658,22 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
 void LLVertexBuffer::initClass(bool use_vbo, bool no_vbo_mapping)
 {
 	sEnableVBOs = use_vbo && gGLManager.mHasVertexBufferObject ;
-	if(sEnableVBOs)
-	{
-		//llassert_always(glBindBufferARB) ; //double check the extention for VBO is loaded.
-
-		llinfos << "VBO is enabled." << llendl ;
-	}
-	else
-	{
-		llinfos << "VBO is disabled." << llendl ;
-	}
-
 	sDisableVBOMapping = sEnableVBOs && no_vbo_mapping ;
 
 	if(!sPrivatePoolp)
 	{ 
 		sPrivatePoolp = LLPrivateMemoryPoolManager::getInstance()->newPool(LLPrivateMemoryPool::STATIC) ;
 	}
+
+	sStreamVBOPool.mType = GL_ARRAY_BUFFER_ARB;
+	sStreamVBOPool.mUsage= GL_STREAM_DRAW_ARB;
+	sStreamIBOPool.mType = GL_ELEMENT_ARRAY_BUFFER_ARB;
+	sStreamIBOPool.mUsage= GL_STREAM_DRAW_ARB;
+
+	sDynamicVBOPool.mType = GL_ARRAY_BUFFER_ARB;
+	sDynamicVBOPool.mUsage= GL_DYNAMIC_DRAW_ARB;
+	sDynamicIBOPool.mType = GL_ELEMENT_ARRAY_BUFFER_ARB;
+	sDynamicIBOPool.mUsage= GL_DYNAMIC_DRAW_ARB;
 }
 
 //static 
@@ -600,7 +711,11 @@ void LLVertexBuffer::cleanupClass()
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
 	unbind();
-	clientCopy(); // deletes GL buffers
+	
+	sStreamIBOPool.cleanup();
+	sDynamicIBOPool.cleanup();
+	sStreamVBOPool.cleanup();
+	sDynamicVBOPool.cleanup();
 
 	if(sPrivatePoolp)
 	{
@@ -609,15 +724,6 @@ void LLVertexBuffer::cleanupClass()
 	}
 }
 
-void LLVertexBuffer::clientCopy(F64 max_time)
-{
-	if (!sDeleteList.empty())
-	{
-		glDeleteBuffersARB(sDeleteList.size(), (GLuint*) &(sDeleteList[0]));
-		sDeleteList.clear();
-	}
-}
-
 //----------------------------------------------------------------------------
 
 LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
@@ -625,8 +731,6 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
 
 	mNumVerts(0),
 	mNumIndices(0),
-	mRequestedNumVerts(-1),
-	mRequestedNumIndices(-1),
 	mUsage(usage),
 	mGLBuffer(0),
 	mGLArray(0),
@@ -636,10 +740,7 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
 	mVertexLocked(FALSE),
 	mIndexLocked(FALSE),
 	mFinal(FALSE),
-	mFilthy(FALSE),
 	mEmpty(TRUE),
-	mResized(FALSE),
-	mDynamicSize(FALSE),
 	mFence(NULL)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);
@@ -664,6 +765,11 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
 		mUsage = GL_STREAM_DRAW_ARB;
 	}
 
+	if (mUsage && mUsage != GL_STREAM_DRAW_ARB)
+	{ //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
+		mUsage = GL_DYNAMIC_DRAW_ARB;
+	}
+		
 	//zero out offsets
 	for (U32 i = 0; i < TYPE_MAX; i++)
 	{
@@ -672,6 +778,7 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
 
 	mTypeMask = typemask;
 	mSize = 0;
+	mIndicesSize = 0;
 	mAlignedOffset = 0;
 	mAlignedIndexOffset = 0;
 
@@ -775,39 +882,35 @@ void LLVertexBuffer::waitFence() const
 
 //----------------------------------------------------------------------------
 
-void LLVertexBuffer::genBuffer()
+void LLVertexBuffer::genBuffer(U32 size)
 {
+	mSize = nhpo2(size);
+
 	if (mUsage == GL_STREAM_DRAW_ARB)
 	{
-		mGLBuffer = sStreamVBOPool.allocate();
-	}
-	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
-	{
-		mGLBuffer = sDynamicVBOPool.allocate();
+		mMappedData = sStreamVBOPool.allocate(mGLBuffer, mSize);
 	}
 	else
 	{
-		BOOST_STATIC_ASSERT(sizeof(mGLBuffer) == sizeof(GLuint));
-		glGenBuffersARB(1, (GLuint*)&mGLBuffer);
+		mMappedData = sDynamicVBOPool.allocate(mGLBuffer, mSize);
 	}
+	
 	sGLCount++;
 }
 
-void LLVertexBuffer::genIndices()
+void LLVertexBuffer::genIndices(U32 size)
 {
+	mIndicesSize = nhpo2(size);
+
 	if (mUsage == GL_STREAM_DRAW_ARB)
 	{
-		mGLIndices = sStreamIBOPool.allocate();
-	}
-	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
-	{
-		mGLIndices = sDynamicIBOPool.allocate();
+		mMappedIndexData = sStreamIBOPool.allocate(mGLIndices, mIndicesSize);
 	}
 	else
 	{
-		BOOST_STATIC_ASSERT(sizeof(mGLBuffer) == sizeof(GLuint));
-		glGenBuffersARB(1, (GLuint*)&mGLIndices);
+		mMappedIndexData = sDynamicIBOPool.allocate(mGLIndices, mIndicesSize);
 	}
+	
 	sGLCount++;
 }
 
@@ -815,16 +918,16 @@ void LLVertexBuffer::releaseBuffer()
 {
 	if (mUsage == GL_STREAM_DRAW_ARB)
 	{
-		sStreamVBOPool.release(mGLBuffer);
-	}
-	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
-	{
-		sDynamicVBOPool.release(mGLBuffer);
+		sStreamVBOPool.release(mGLBuffer, mMappedData, mSize);
 	}
 	else
 	{
-		sDeleteList.push_back(mGLBuffer);
+		sDynamicVBOPool.release(mGLBuffer, mMappedData, mSize);
 	}
+	
+	mGLBuffer = 0;
+	mMappedData = NULL;
+
 	sGLCount--;
 }
 
@@ -832,24 +935,23 @@ void LLVertexBuffer::releaseIndices()
 {
 	if (mUsage == GL_STREAM_DRAW_ARB)
 	{
-		sStreamIBOPool.release(mGLIndices);
+		sStreamIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
 	}
 	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
 	{
-		sDynamicIBOPool.release(mGLIndices);
-	}
-	else
-	{
-		sDeleteList.push_back(mGLIndices);
+		sDynamicIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
 	}
+
+	mGLIndices = 0;
+	mMappedIndexData = NULL;
+	
 	sGLCount--;
 }
 
-void LLVertexBuffer::createGLBuffer()
+void LLVertexBuffer::createGLBuffer(U32 size)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES);
 	
-	U32 size = getSize();
 	if (mGLBuffer)
 	{
 		destroyGLBuffer();
@@ -864,23 +966,21 @@ void LLVertexBuffer::createGLBuffer()
 
 	if (useVBOs())
 	{
-		mMappedData = NULL;
-		genBuffer();
-		mResized = TRUE;
+		genBuffer(size);
 	}
 	else
 	{
 		static int gl_buffer_idx = 0;
 		mGLBuffer = ++gl_buffer_idx;
 		mMappedData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
+		mSize = size;
 	}
 }
 
-void LLVertexBuffer::createGLIndices()
+void LLVertexBuffer::createGLIndices(U32 size)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES);
-	U32 size = getIndicesSize();
-
+	
 	if (mGLIndices)
 	{
 		destroyGLIndices();
@@ -900,15 +1000,14 @@ void LLVertexBuffer::createGLIndices()
 	{
 		//pad by another 16 bytes for VBO pointer adjustment
 		size += 16;
-		mMappedIndexData = NULL;
-		genIndices();
-		mResized = TRUE;
+		genIndices(size);
 	}
 	else
 	{
 		mMappedIndexData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
 		static int gl_buffer_idx = 0;
 		mGLIndices = ++gl_buffer_idx;
+		mIndicesSize = size;
 	}
 }
 
@@ -919,12 +1018,6 @@ void LLVertexBuffer::destroyGLBuffer()
 	{
 		if (useVBOs())
 		{
-			freeClientBuffer() ;
-
-			if (mMappedData || mMappedIndexData)
-			{
-				llerrs << "Vertex buffer destroyed while mapped!" << llendl;
-			}
 			releaseBuffer();
 		}
 		else
@@ -933,8 +1026,6 @@ void LLVertexBuffer::destroyGLBuffer()
 			mMappedData = NULL;
 			mEmpty = TRUE;
 		}
-
-		sAllocatedBytes -= getSize();
 	}
 	
 	mGLBuffer = 0;
@@ -948,12 +1039,6 @@ void LLVertexBuffer::destroyGLIndices()
 	{
 		if (useVBOs())
 		{
-			freeClientBuffer() ;
-
-			if (mMappedData || mMappedIndexData)
-			{
-				llerrs << "Vertex buffer destroyed while mapped." << llendl;
-			}
 			releaseIndices();
 		}
 		else
@@ -962,8 +1047,6 @@ void LLVertexBuffer::destroyGLIndices()
 			mMappedIndexData = NULL;
 			mEmpty = TRUE;
 		}
-
-		sAllocatedBytes -= getIndicesSize();
 	}
 
 	mGLIndices = 0;
@@ -982,23 +1065,14 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
 		nverts = 65535;
 	}
 
-	mRequestedNumVerts = nverts;
+	U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts);
 
-	if (!mDynamicSize)
+	if (needed_size > mSize || needed_size <= mSize/2)
 	{
-		mNumVerts = nverts;
-	}
-	else if (mUsage == GL_STATIC_DRAW_ARB ||
-		nverts > mNumVerts ||
-		nverts < mNumVerts/2)
-	{
-		if (mUsage != GL_STATIC_DRAW_ARB && nverts + nverts/4 <= 65535)
-		{
-			nverts += nverts/4;
-		}
-		mNumVerts = nverts;
+		createGLBuffer(needed_size);
 	}
-	mSize = calcOffsets(mTypeMask, mOffsets, mNumVerts);
+
+	mNumVerts = nverts;
 }
 
 void LLVertexBuffer::updateNumIndices(S32 nindices)
@@ -1007,22 +1081,14 @@ void LLVertexBuffer::updateNumIndices(S32 nindices)
 
 	llassert(nindices >= 0);
 
-	mRequestedNumIndices = nindices;
-	if (!mDynamicSize)
+	U32 needed_size = sizeof(U16) * nindices;
+
+	if (needed_size > mIndicesSize || needed_size <= mIndicesSize/2)
 	{
-		mNumIndices = nindices;
+		createGLIndices(needed_size);
 	}
-	else if (mUsage == GL_STATIC_DRAW_ARB ||
-		nindices > mNumIndices ||
-		nindices < mNumIndices/2)
-	{
-		if (mUsage != GL_STATIC_DRAW_ARB)
-		{
-			nindices += nindices/4;
-		}
 
-		mNumIndices = nindices;
-	}
+	mNumIndices = nindices;
 }
 
 void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
@@ -1040,15 +1106,8 @@ void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
 	updateNumVerts(nverts);
 	updateNumIndices(nindices);
 	
-	if (mMappedData)
-	{
-		llerrs << "LLVertexBuffer::allocateBuffer() called redundantly." << llendl;
-	}
 	if (create && (nverts || nindices))
 	{
-		createGLBuffer();
-		createGLIndices();
-
 		//actually allocate space for the vertex buffer if using VBO mapping
 		flush();
 
@@ -1060,8 +1119,6 @@ void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
 			setupVertexArray();
 		}
 	}
-	
-	sAllocatedBytes += getSize() + getIndicesSize();
 }
 
 void LLVertexBuffer::setupVertexArray()
@@ -1151,77 +1208,13 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
 	llassert(newnverts >= 0);
 	llassert(newnindices >= 0);
 
-	mRequestedNumVerts = newnverts;
-	mRequestedNumIndices = newnindices;
-
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_RESIZE_BUFFER);
-	mDynamicSize = TRUE;
-	if (mUsage == GL_STATIC_DRAW_ARB)
-	{ //always delete/allocate static buffers on resize
-		destroyGLBuffer();
-		destroyGLIndices();
-		allocateBuffer(newnverts, newnindices, TRUE);
-		mFinal = FALSE;
-	}
-	else if (newnverts > mNumVerts || newnindices > mNumIndices ||
-			 newnverts < mNumVerts/2 || newnindices < mNumIndices/2)
-	{
-		sAllocatedBytes -= getSize() + getIndicesSize();
-		
-		updateNumVerts(newnverts);		
-		updateNumIndices(newnindices);
-		
-		S32 newsize = getSize();
-		S32 new_index_size = getIndicesSize();
-
-		sAllocatedBytes += newsize + new_index_size;
-
-		if (newsize)
-		{
-			if (!mGLBuffer)
-			{ //no buffer exists, create a new one
-				createGLBuffer();
-			}
-			else
-			{
-				if (!useVBOs())
-				{
-					FREE_MEM(sPrivatePoolp, mMappedData);
-					mMappedData = (U8*)ALLOCATE_MEM(sPrivatePoolp, newsize);
-				}
-				mResized = TRUE;
-			}
-		}
-		else if (mGLBuffer)
-		{
-			destroyGLBuffer();
-		}
-		
-		if (new_index_size)
-		{
-			if (!mGLIndices)
-			{
-				createGLIndices();
-			}
-			else
-			{
-				if (!useVBOs())
-				{
-					FREE_MEM(sPrivatePoolp, mMappedIndexData) ;
-					mMappedIndexData = (U8*)ALLOCATE_MEM(sPrivatePoolp, new_index_size);
-				}
-				mResized = TRUE;
-			}
-		}
-		else if (mGLIndices)
-		{
-			destroyGLIndices();
-		}
-	}
-
-	if (mResized && useVBOs())
+	
+	updateNumVerts(newnverts);		
+	updateNumIndices(newnindices);
+	
+	if (useVBOs())
 	{
-		freeClientBuffer();
 		flush();
 
 		if (mGLArray)
@@ -1244,32 +1237,6 @@ BOOL LLVertexBuffer::useVBOs() const
 }
 
 //----------------------------------------------------------------------------
-void LLVertexBuffer::freeClientBuffer()
-{
-	if(useVBOs() && sDisableVBOMapping && (mMappedData || mMappedIndexData))
-	{
-		FREE_MEM(sPrivatePoolp, mMappedData) ;
-		FREE_MEM(sPrivatePoolp, mMappedIndexData) ;
-		mMappedData = NULL ;
-		mMappedIndexData = NULL ;
-	}
-}
-
-void LLVertexBuffer::allocateClientVertexBuffer()
-{
-	if(!mMappedData)
-	{
-		mMappedData = (U8*)ALLOCATE_MEM(sPrivatePoolp, getSize());
-	}
-}
-
-void LLVertexBuffer::allocateClientIndexBuffer()
-{
-	if(!mMappedIndexData)
-	{
-		mMappedIndexData = (U8*)ALLOCATE_MEM(sPrivatePoolp, getIndicesSize());		
-	}
-}
 
 bool expand_region(LLVertexBuffer::MappedRegion& region, S32 index, S32 count)
 {
@@ -1350,7 +1317,6 @@ U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_ran
 			if(sDisableVBOMapping)
 			{
 				map_range = false;
-				allocateClientVertexBuffer() ;
 			}
 			else
 			{
@@ -1535,7 +1501,6 @@ U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
 			if(sDisableVBOMapping)
 			{
 				map_range = false;
-				allocateClientIndexBuffer() ;
 			}
 			else
 			{
@@ -1772,21 +1737,7 @@ void LLVertexBuffer::unmapBuffer()
 
 	if(updated_all)
 	{
-		if(mUsage == GL_STATIC_DRAW_ARB)
-		{
-			//static draw buffers can only be mapped a single time
-			//throw out client data (we won't be using it again)
-			mEmpty = TRUE;
-			mFinal = TRUE;
-			if(sDisableVBOMapping)
-			{
-				freeClientBuffer() ;
-			}
-		}
-		else
-		{
-			mEmpty = FALSE;
-		}
+		mEmpty = FALSE;
 	}
 }
 
@@ -1965,27 +1916,6 @@ void LLVertexBuffer::flush()
 {
 	if (useVBOs())
 	{
-		if (mResized)
-		{
-			if (mGLBuffer)
-			{
-				stop_glerror();
-				bindGLBuffer(true);
-				glBufferDataARB(GL_ARRAY_BUFFER_ARB, getSize(), NULL, mUsage);
-				stop_glerror();
-			}
-			if (mGLIndices)
-			{
-				stop_glerror();
-				bindGLIndices(true);
-				glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, getIndicesSize(), NULL, mUsage);
-				stop_glerror();
-			}
-
-			mEmpty = TRUE;
-			mResized = FALSE;
-		}
-
 		unmapBuffer();
 	}
 }
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index b50c409c49..3e6f6a959a 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -51,24 +51,32 @@
 
 //============================================================================
 // gl name pools for dynamic and streaming buffers
-class LLVBOPool : public LLGLNamePool
+class LLVBOPool
 {
-protected:
-	virtual GLuint allocateName()
-	{
-		GLuint name;
-		stop_glerror();
-		glGenBuffersARB(1, &name);
-		stop_glerror();
-		return name;
-	}
+public:
+	static U32 sBytesPooled;
+
+	U32 mUsage;
+	U32 mType;
 
-	virtual void releaseName(GLuint name)
+	//size MUST be a power of 2
+	U8* allocate(U32& name, U32 size);
+	
+	//size MUST be the size provided to allocate that returned the given name
+	void release(U32 name, U8* buffer, U32 size);
+	
+	//destroy all records in mFreeList
+	void cleanup();
+
+	class Record
 	{
-		stop_glerror();
-		glDeleteBuffersARB(1, &name);
-		stop_glerror();
-	}
+	public:
+		U32 mGLName;
+		U8* mClientData;
+	};
+
+	typedef std::list<Record> record_list_t;
+	std::vector<record_list_t> mFreeList;
 };
 
 class LLGLFence
@@ -120,8 +128,7 @@ public:
 	static void drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm);
 	static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp);
 
- 	static void clientCopy(F64 max_time = 0.005); //copy data from client to GL
-	static void unbind(); //unbind any bound vertex buffer
+ 	static void unbind(); //unbind any bound vertex buffer
 
 	//get the size of a vertex with the given typemask
 	static S32 calcVertexSize(const U32& typemask);
@@ -181,25 +188,22 @@ protected:
 	virtual void setupVertexBuffer(U32 data_mask); // pure virtual, called from mapBuffer()
 	void setupVertexArray();
 	
-	void	genBuffer();
-	void	genIndices();
+	void	genBuffer(U32 size);
+	void	genIndices(U32 size);
 	bool	bindGLBuffer(bool force_bind = false);
 	bool	bindGLIndices(bool force_bind = false);
 	bool	bindGLArray();
 	void	releaseBuffer();
 	void	releaseIndices();
-	void	createGLBuffer();
-	void	createGLIndices();
+	void	createGLBuffer(U32 size);
+	void	createGLIndices(U32 size);
 	void 	destroyGLBuffer();
 	void 	destroyGLIndices();
 	void	updateNumVerts(S32 nverts);
 	void	updateNumIndices(S32 nindices); 
 	virtual BOOL	useVBOs() const;
 	void	unmapBuffer();
-	void freeClientBuffer() ;
-	void allocateClientVertexBuffer() ;
-	void allocateClientIndexBuffer() ;
-
+		
 public:
 	LLVertexBuffer(U32 typemask, S32 usage);
 	
@@ -239,15 +243,13 @@ public:
 	BOOL isLocked() const					{ return mVertexLocked || mIndexLocked; }
 	S32 getNumVerts() const					{ return mNumVerts; }
 	S32 getNumIndices() const				{ return mNumIndices; }
-	S32 getRequestedVerts() const			{ return mRequestedNumVerts; }
-	S32 getRequestedIndices() const			{ return mRequestedNumIndices; }
-
+	
 	U8* getIndicesPointer() const			{ return useVBOs() ? (U8*) mAlignedIndexOffset : mMappedIndexData; }
 	U8* getVerticesPointer() const			{ return useVBOs() ? (U8*) mAlignedOffset : mMappedData; }
 	U32 getTypeMask() const					{ return mTypeMask; }
 	bool hasDataType(S32 type) const		{ return ((1 << type) & getTypeMask()); }
 	S32 getSize() const;
-	S32 getIndicesSize() const				{ return mNumIndices * sizeof(U16); }
+	S32 getIndicesSize() const				{ return mIndicesSize; }
 	U8* getMappedData() const				{ return mMappedData; }
 	U8* getMappedIndices() const			{ return mMappedIndexData; }
 	S32 getOffset(S32 type) const			{ return mOffsets[type]; }
@@ -265,12 +267,11 @@ public:
 protected:	
 	S32		mNumVerts;		// Number of vertices allocated
 	S32		mNumIndices;	// Number of indices allocated
-	S32		mRequestedNumVerts;  // Number of vertices requested
-	S32		mRequestedNumIndices;  // Number of indices requested
-
+	
 	ptrdiff_t mAlignedOffset;
 	ptrdiff_t mAlignedIndexOffset;
 	S32		mSize;
+	S32		mIndicesSize;
 	U32		mTypeMask;
 	S32		mUsage;			// GL usage
 	U32		mGLBuffer;		// GL VBO handle
@@ -282,10 +283,7 @@ protected:
 	BOOL	mVertexLocked;			// if TRUE, vertex buffer is being or has been written to in client memory
 	BOOL	mIndexLocked;			// if TRUE, index buffer is being or has been written to in client memory
 	BOOL	mFinal;			// if TRUE, buffer can not be mapped again
-	BOOL	mFilthy;		// if TRUE, entire buffer must be copied (used to prevent redundant dirty flags)
 	BOOL	mEmpty;			// if TRUE, client buffer is empty (or NULL). Old values have been discarded.	
-	BOOL	mResized;		// if TRUE, client buffer has been resized and GL buffer has not
-	BOOL	mDynamicSize;	// if TRUE, buffer has been resized at least once (and should be padded)
 	S32		mOffsets[TYPE_MAX];
 
 	std::vector<MappedRegion> mMappedVertexRegions;
@@ -305,7 +303,6 @@ public:
 	static S32 sGLCount;
 	static S32 sMappedCount;
 	static BOOL sMapped;
-	static std::vector<U32> sDeleteList;
 	typedef std::list<LLVertexBuffer*> buffer_list_t;
 		
 	static BOOL sDisableVBOMapping; //disable glMapBufferARB
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index a710bdcdbd..7290a48a1a 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -1277,8 +1277,8 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*
 	
 	if (buffer.isNull() || 
 		buffer->getTypeMask() != data_mask ||
-		buffer->getRequestedVerts() != vol_face.mNumVertices ||
-		buffer->getRequestedIndices() != vol_face.mNumIndices ||
+		buffer->getNumVerts() != vol_face.mNumVertices ||
+		buffer->getNumIndices() != vol_face.mNumIndices ||
 		(drawable && drawable->isState(LLDrawable::REBUILD_ALL)))
 	{
 		face->setGeomIndex(0);
@@ -1366,7 +1366,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*
 		LLMatrix4a bind_shape_matrix;
 		bind_shape_matrix.loadu(skin->mBindShapeMatrix);
 
-		for (U32 j = 0; j < buffer->getRequestedVerts(); ++j)
+		for (U32 j = 0; j < buffer->getNumVerts(); ++j)
 		{
 			LLMatrix4a final_mat;
 			final_mat.clear();
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
index 3fe5b4d929..cdf6e1ab52 100644
--- a/indra/newview/lldrawpooltree.cpp
+++ b/indra/newview/lldrawpooltree.cpp
@@ -113,8 +113,8 @@ void LLDrawPoolTree::render(S32 pass)
 			if(buff)
 			{
 				buff->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK);
-				buff->drawRange(LLRender::TRIANGLES, 0, buff->getRequestedVerts()-1, buff->getRequestedIndices(), 0); 
-				gPipeline.addTrianglesDrawn(buff->getRequestedIndices());
+				buff->drawRange(LLRender::TRIANGLES, 0, buff->getNumVerts()-1, buff->getNumIndices(), 0); 
+				gPipeline.addTrianglesDrawn(buff->getNumIndices());
 			}
 		}
 	}
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 36b88ebbd4..eab3dcfadd 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -362,8 +362,8 @@ void LLFace::setSize(S32 num_vertices, S32 num_indices, bool align)
 {
 	if (align)
 	{
-		//allocate vertices in blocks of 16 for alignment
-		num_vertices = (num_vertices + 0xF) & ~0xF;
+		//allocate vertices in blocks of 4 for alignment
+		num_vertices = (num_vertices + 0x3) & ~0x3;
 	}
 	
 	if (mGeomCount != num_vertices ||
@@ -1073,6 +1073,11 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	S32 num_vertices = (S32)vf.mNumVertices;
 	S32 num_indices = (S32) vf.mNumIndices;
 	
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+	{
+		updateRebuildFlags();
+	}
+
 	bool map_range = gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange;
 
 	if (mVertexBuffer.notNull())
@@ -2055,7 +2060,7 @@ BOOL LLFace::verify(const U32* indices_array) const
 	}
 	
 	// First, check whether the face data fits within the pool's range.
-	if ((mGeomIndex + mGeomCount) > mVertexBuffer->getRequestedVerts())
+	if ((mGeomIndex + mGeomCount) > mVertexBuffer->getNumVerts())
 	{
 		ok = FALSE;
 		llinfos << "Face references invalid vertices!" << llendl;
@@ -2074,7 +2079,7 @@ BOOL LLFace::verify(const U32* indices_array) const
 		llinfos << "Face has bogus indices count" << llendl;
 	}
 	
-	if (mIndicesIndex + mIndicesCount > mVertexBuffer->getRequestedIndices())
+	if (mIndicesIndex + mIndicesCount > mVertexBuffer->getNumIndices())
 	{
 		ok = FALSE;
 		llinfos << "Face references invalid indices!" << llendl;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index d9ce72a2c2..8f6013f2d9 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -5457,7 +5457,7 @@ BOOL LLModelPreview::render()
 								}
 							}
 
-							for (U32 j = 0; j < buffer->getRequestedVerts(); ++j)
+							for (U32 j = 0; j < buffer->getNumVerts(); ++j)
 							{
 								LLMatrix4 final_mat;
 								final_mat.mMatrix[0][0] = final_mat.mMatrix[1][1] = final_mat.mMatrix[2][2] = final_mat.mMatrix[3][3] = 0.f;
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index ffb0ce4056..2530f1f0d4 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -2454,7 +2454,7 @@ void pushBufferVerts(LLVertexBuffer* buffer, U32 mask)
 	if (buffer)
 	{
 		buffer->setBuffer(mask);
-		buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getRequestedVerts()-1, buffer->getRequestedIndices(), 0);
+		buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 	}
 }
 
@@ -2526,7 +2526,7 @@ void renderOctree(LLSpatialGroup* group)
 	//coded by buffer usage and activity
 	gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
 	LLVector4 col;
-	/*if (group->mBuilt > 0.f)
+	if (group->mBuilt > 0.f)
 	{
 		group->mBuilt -= 2.f * gFrameIntervalSeconds;
 		if (group->mBufferUsage == GL_STATIC_DRAW_ARB)
@@ -2595,7 +2595,7 @@ void renderOctree(LLSpatialGroup* group)
 			gGL.diffuseColor4f(1,1,1,1);
 		}
 	}
-	else*/
+	else
 	{
 		if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->getData().empty() 
 			&& group->mSpatialPartition->mRenderByGroup)
@@ -3442,11 +3442,11 @@ void renderPhysicsShapes(LLSpatialGroup* group)
 
 						buff->setBuffer(LLVertexBuffer::MAP_VERTEX);
 						gGL.diffuseColor3f(0.2f, 0.5f, 0.3f);
-						buff->draw(LLRender::TRIANGLES, buff->getRequestedIndices(), 0);
+						buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
 									
 						gGL.diffuseColor3f(0.2f, 1.f, 0.3f);
 						glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-						buff->draw(LLRender::TRIANGLES, buff->getRequestedIndices(), 0);
+						buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
 					}
 				}
 			}
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 1832416a4b..e0359cc61d 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -661,10 +661,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 		
 		{ 
 			LLMemType mt_ds(LLMemType::MTYPE_DISPLAY_SWAP);
-			{
- 				LLFastTimer ftm(FTM_CLIENT_COPY);
-				LLVertexBuffer::clientCopy(0.016);
-			}
 
 			if (gResizeScreenTexture)
 			{
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 2aac43d99e..f3e9bc711a 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -532,7 +532,7 @@ public:
 			
 			}
 
-			addText(xpos, ypos, llformat("%d MB Vertex Data", LLVertexBuffer::sAllocatedBytes/(1024*1024)));
+			addText(xpos, ypos, llformat("%d MB Vertex Data (%d MB Pooled)", LLVertexBuffer::sAllocatedBytes/(1024*1024), LLVBOPool::sBytesPooled/(1024*1024)));
 			ypos += y_inc;
 
 			addText(xpos, ypos, llformat("%d Vertex Buffers", LLVertexBuffer::sGLCount));
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 5687ba5064..6506938766 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -2117,8 +2117,8 @@ void LLVOAvatar::updateMeshData()
 			}
 			else
 			{
-				if (buff->getRequestedIndices() == num_indices &&
-					buff->getRequestedVerts() == num_vertices)
+				if (buff->getNumIndices() == num_indices &&
+					buff->getNumVerts() == num_vertices)
 				{
 					terse_update = true;
 				}
diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp
index 14fd0a1eb1..f1c5499d84 100644
--- a/indra/newview/llvowlsky.cpp
+++ b/indra/newview/llvowlsky.cpp
@@ -516,9 +516,9 @@ void LLVOWLSky::drawDome(void)
 
 		strips_segment->drawRange(
 			LLRender::TRIANGLE_STRIP, 
-			0, strips_segment->getRequestedVerts()-1, strips_segment->getRequestedIndices(), 
+			0, strips_segment->getNumVerts()-1, strips_segment->getNumIndices(), 
 			0);
-		gPipeline.addTrianglesDrawn(strips_segment->getRequestedIndices(), LLRender::TRIANGLE_STRIP);
+		gPipeline.addTrianglesDrawn(strips_segment->getNumIndices(), LLRender::TRIANGLE_STRIP);
 	}
 
 #else
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index e4125c8dc8..5035e0197d 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -264,15 +264,7 @@ std::string gPoolNames[] =
 
 void drawBox(const LLVector3& c, const LLVector3& r);
 void drawBoxOutline(const LLVector3& pos, const LLVector3& size);
-
-U32 nhpo2(U32 v) 
-{
-	U32 r = 1;
-	while (r < v) {
-		r *= 2;
-	}
-	return r;
-}
+U32 nhpo2(U32 v);
 
 glh::matrix4f glh_copy_matrix(F32* src)
 {
@@ -2902,11 +2894,7 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result)
 			}
 		}
 	}
-	{
-		LLFastTimer ftm(FTM_CLIENT_COPY);
-		LLVertexBuffer::clientCopy();
-	}
-	
+		
 	postSort(camera);	
 }
 
@@ -6122,13 +6110,7 @@ void LLPipeline::resetVertexBuffers()
 		llwarns << "VBO wipe failed." << llendl;
 	}
 
-	if (!LLVertexBuffer::sStreamIBOPool.mNameList.empty() ||
-		!LLVertexBuffer::sStreamVBOPool.mNameList.empty() ||
-		!LLVertexBuffer::sDynamicIBOPool.mNameList.empty() ||
-		!LLVertexBuffer::sDynamicVBOPool.mNameList.empty())
-	{
-		llwarns << "VBO name pool cleanup failed." << llendl;
-	}
+	llassert(LLVertexBuffer::sGLCount == 0);
 
 	LLVertexBuffer::unbind();	
 	
@@ -6142,6 +6124,8 @@ void LLPipeline::resetVertexBuffers()
 	sBakeSunlight = gSavedSettings.getBOOL("RenderBakeSunlight");
 	sNoAlpha = gSavedSettings.getBOOL("RenderNoAlpha");
 	LLPipeline::sTextureBindTest = gSavedSettings.getBOOL("RenderDebugTextureBind");
+
+	LLVertexBuffer::initClass(LLVertexBuffer::sEnableVBOs, LLVertexBuffer::sDisableVBOMapping);
 }
 
 void LLPipeline::renderObjects(U32 type, U32 mask, BOOL texture)
-- 
GitLab