Skip to content
Snippets Groups Projects
llvertexbuffer.cpp 48.3 KiB
Newer Older
/** 
 * @file llvertexbuffer.cpp
 * @brief LLVertexBuffer implementation
 *
 * $LicenseInfo:firstyear=2003&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 "linden_common.h"

#include "llvertexbuffer.h"
// #include "llrender.h"
#include "llglheaders.h"
#include "llmemtype.h"
#include "llrender.h"
David Parks's avatar
David Parks committed
#include "llvector4a.h"
#include "llmemory.h"

//============================================================================

//static
LLVBOPool LLVertexBuffer::sStreamVBOPool;
LLVBOPool LLVertexBuffer::sDynamicVBOPool;
LLVBOPool LLVertexBuffer::sStreamIBOPool;
LLVBOPool LLVertexBuffer::sDynamicIBOPool;

Xiaohong Bao's avatar
Xiaohong Bao committed
LLPrivateMemoryPool* LLVertexBuffer::sPrivatePoolp = NULL ;
U32 LLVertexBuffer::sBindCount = 0;
U32 LLVertexBuffer::sSetCount = 0;
S32 LLVertexBuffer::sCount = 0;
S32 LLVertexBuffer::sGLCount = 0;
S32 LLVertexBuffer::sMappedCount = 0;
BOOL LLVertexBuffer::sDisableVBOMapping = FALSE ;
BOOL LLVertexBuffer::sEnableVBOs = TRUE;
U32 LLVertexBuffer::sGLRenderBuffer = 0;
U32 LLVertexBuffer::sGLRenderIndices = 0;
U32 LLVertexBuffer::sLastMask = 0;
BOOL LLVertexBuffer::sVBOActive = FALSE;
BOOL LLVertexBuffer::sIBOActive = FALSE;
U32 LLVertexBuffer::sAllocatedBytes = 0;
BOOL LLVertexBuffer::sMapped = FALSE;
BOOL LLVertexBuffer::sUseStreamDraw = TRUE;
BOOL LLVertexBuffer::sPreferStreamDraw = FALSE;

std::vector<U32> LLVertexBuffer::sDeleteList;

David Parks's avatar
David Parks committed

const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000;  //1 ms

class LLGLSyncFence : public LLGLFence
{
public:
Leslie Linden's avatar
Leslie Linden committed
#ifdef GL_ARB_sync
Leslie Linden's avatar
Leslie Linden committed
#endif
Leslie Linden's avatar
Leslie Linden committed
#ifdef GL_ARB_sync
Leslie Linden's avatar
Leslie Linden committed
#endif
		if (mSync)
		{
			glDeleteSync(mSync);
		}
		mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
		if (mSync)
		{
			while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
			{ //track the number of times we've waited here
				static S32 waits = 0;
				waits++;
			}
		}
Leslie Linden's avatar
Leslie Linden committed
#endif
S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
David Parks's avatar
David Parks committed
	sizeof(LLVector4), // TYPE_VERTEX,
	sizeof(LLVector4), // TYPE_NORMAL,
	sizeof(LLVector2), // TYPE_TEXCOORD0,
	sizeof(LLVector2), // TYPE_TEXCOORD1,
	sizeof(LLVector2), // TYPE_TEXCOORD2,
	sizeof(LLVector2), // TYPE_TEXCOORD3,
	sizeof(LLColor4U), // TYPE_COLOR,
	sizeof(U8),		   // TYPE_EMISSIVE
David Parks's avatar
David Parks committed
	sizeof(LLVector4), // TYPE_BINORMAL,
	sizeof(F32),	   // TYPE_WEIGHT,
David Parks's avatar
David Parks committed
	sizeof(LLVector4), // TYPE_WEIGHT4,
	sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
};

U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] = 
{
	GL_TRIANGLES,
	GL_TRIANGLE_STRIP,
	GL_TRIANGLE_FAN,
	GL_POINTS,
	GL_LINES,
	GL_LINE_STRIP,
	GL_QUADS,
	GL_LINE_LOOP,
};

//static
void LLVertexBuffer::setupClientArrays(U32 data_mask)
{
	/*if (LLGLImmediate::sStarted)
	{
		llerrs << "Cannot use LLGLImmediate and LLVertexBuffer simultaneously!" << llendl;
	LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;

	if (sLastMask != data_mask)
	{
		llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL);
		static LLGLSLShader* last_shader = LLGLSLShader::sCurBoundShaderPtr;
		llassert(sLastMask == 0 || last_shader == shader);
		last_shader = shader;

		U32 mask[] =
		{
			MAP_VERTEX,
			MAP_NORMAL,
			MAP_EMISSIVE,
			MAP_WEIGHT,
			MAP_WEIGHT4,
			MAP_BINORMAL,
			MAP_CLOTHWEIGHT,
		U32 type[] =
		{
			TYPE_VERTEX,
			TYPE_NORMAL,
			TYPE_TEXCOORD0,
			TYPE_COLOR,
			TYPE_EMISSIVE,
			TYPE_WEIGHT,
			TYPE_WEIGHT4,
			TYPE_BINORMAL,
			TYPE_CLOTHWEIGHT,
		};

		GLenum array[] =
		{
			GL_VERTEX_ARRAY,
			GL_NORMAL_ARRAY,
			GL_TEXTURE_COORD_ARRAY,
		for (U32 i = 0; i < 9; ++i)
			S32 loc = -1;
			if (shader)
			{
				loc = shader->getAttribLocation(type[i]);
			}

			if (sLastMask & mask[i])
			{ //was enabled
				if (!(data_mask & mask[i]))
				{ //needs to be disabled
					if (loc >= 0)
					{
						glDisableVertexAttribArrayARB(loc);
					}
					else if (!shader)
					{
						glDisableClientState(array[i]);
					}
				else if (gDebugGL && !shader && array[i])
				{ //needs to be enabled, make sure it was (DEBUG)
					if (loc < 0 && !glIsEnabled(array[i]))
						if (gDebugSession)
						{
							error = TRUE;
							gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl;
						}
						else
						{
							llerrs << "Bad client state! " << array[i] << " disabled." << llendl;
						}
				{ //needs to be enabled
					if (loc >= 0)
					{
						glEnableVertexAttribArrayARB(loc);
					}
					else if (!shader)
					{
						glEnableClientState(array[i]);
					}
				else if (!shader && array[i] && gDebugGL && glIsEnabled(array[i]))
				{ //needs to be disabled, make sure it was (DEBUG TEMPORARY)
					if (gDebugSession)
					{
						error = TRUE;
						gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl;
					}
					else
					{
						llerrs << "Bad client state! " << array[i] << " enabled." << llendl;
					}
		if (error)
		{
			ll_fail("LLVertexBuffer::setupClientArrays failed");
		}

		U32 type_tc[] = 
		{
			TYPE_TEXCOORD1,
			TYPE_TEXCOORD2,
			TYPE_TEXCOORD3
		};

			S32 loc = -1;
			if (shader)
			{
				loc = shader->getAttribLocation(type_tc[i]);
			}

				{ //disable
					if (loc >= 0)
					{
						glDisableVertexAttribArrayARB(loc);
					}
					else if (!shader)
					{
						glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
						glDisableClientState(GL_TEXTURE_COORD_ARRAY);
						glClientActiveTextureARB(GL_TEXTURE0_ARB);
					}
				if (loc >= 0)
				{
					glEnableVertexAttribArrayARB(loc);
				}
				else if (!shader)
				{
					glClientActiveTextureARB(GL_TEXTURE1_ARB+i);
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
					glClientActiveTextureARB(GL_TEXTURE0_ARB);
				}
			if (sLastMask & MAP_BINORMAL)
				if (!(data_mask & MAP_BINORMAL))
				{
					glClientActiveTextureARB(GL_TEXTURE2_ARB);
					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
					glClientActiveTextureARB(GL_TEXTURE0_ARB);
				}
			else if (data_mask & MAP_BINORMAL)
				glClientActiveTextureARB(GL_TEXTURE2_ARB);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				glClientActiveTextureARB(GL_TEXTURE0_ARB);
		sLastMask = data_mask;
	}
}

//static
void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);

	U32 count = pos.size();
	llassert_always(norm.size() >= pos.size());
	llassert_always(count > 0) ;

	unbind();
	
	setupClientArrays(MAP_VERTEX | MAP_NORMAL);

	LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;

	if (shader)
	{
		S32 loc = shader->getAttribLocation(LLVertexBuffer::TYPE_VERTEX);
		if (loc > -1)
		{
			glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
		}
		loc = shader->getAttribLocation(LLVertexBuffer::TYPE_NORMAL);
		if (loc > -1)
		{
			glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[0].mV);
		}
	}
	else
	{
		glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
		glNormalPointer(GL_FLOAT, 0, norm[0].mV);
	}

	glDrawArrays(sGLMode[mode], 0, count);
}
Tofu Linden's avatar
Tofu Linden committed

//static
void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
{
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);

	U32 mask = LLVertexBuffer::MAP_VERTEX;
	if (tc)
	{
		mask = mask | LLVertexBuffer::MAP_TEXCOORD0;
	}

	unbind();
	
	setupClientArrays(mask);

	LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;

	if (shader)
	{
		S32 loc = shader->getAttribLocation(LLVertexBuffer::TYPE_VERTEX);
		if (loc > -1)
		{
			glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);

			if (tc)
			{
				loc = shader->getAttribLocation(LLVertexBuffer::TYPE_TEXCOORD0);
				if (loc > -1)
				{
					glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
				}
			}
		}
	}
	else
	{
		glTexCoordPointer(2, GL_FLOAT, 0, tc);
		glVertexPointer(3, GL_FLOAT, 16, pos);
	}

	glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
}

void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
{
	if (start >= (U32) mRequestedNumVerts ||
Tofu Linden's avatar
Tofu Linden committed
	    end >= (U32) mRequestedNumVerts)
David Parks's avatar
David Parks committed
		llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mRequestedNumVerts << llendl;
Tofu Linden's avatar
Tofu Linden committed
	llassert(mRequestedNumIndices >= 0);

	if (indices_offset >= (U32) mRequestedNumIndices ||
Tofu Linden's avatar
Tofu Linden committed
	    indices_offset + count > (U32) mRequestedNumIndices)
	{
		llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
	}

	if (gDebugGL && !useVBOs())
	{
		U16* idx = ((U16*) getIndicesPointer())+indices_offset;
		for (U32 i = 0; i < count; ++i)
		{
			if (idx[i] < start || idx[i] > end)
			{
				llerrs << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << llendl;
			}
		}
	}
}

void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
{
	validateRange(start, end, count, indices_offset);

	llassert(mRequestedNumVerts >= 0);
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
	if (mGLIndices != sGLRenderIndices)
	{
		llerrs << "Wrong index buffer bound." << llendl;
	}

	if (mGLBuffer != sGLRenderBuffer)
	{
		llerrs << "Wrong vertex buffer bound." << llendl;
	}

Tofu Linden's avatar
Tofu Linden committed
	if (mode >= LLRender::NUM_MODES)
	{
		llerrs << "Invalid draw mode: " << mode << llendl;
David Parks's avatar
David Parks committed
	U16* idx = ((U16*) getIndicesPointer())+indices_offset;

	glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT, 
David Parks's avatar
David Parks committed
		idx);
	stop_glerror();
}

void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
{
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);

Tofu Linden's avatar
Tofu Linden committed
	llassert(mRequestedNumIndices >= 0);
	if (indices_offset >= (U32) mRequestedNumIndices ||
Tofu Linden's avatar
Tofu Linden committed
	    indices_offset + count > (U32) mRequestedNumIndices)
	{
		llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl;
	}

	if (mGLIndices != sGLRenderIndices)
	{
		llerrs << "Wrong index buffer bound." << llendl;
	}

	if (mGLBuffer != sGLRenderBuffer)
	{
		llerrs << "Wrong vertex buffer bound." << llendl;
	}

Tofu Linden's avatar
Tofu Linden committed
	if (mode >= LLRender::NUM_MODES)
	{
		llerrs << "Invalid draw mode: " << mode << llendl;
	glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT,
		((U16*) getIndicesPointer()) + indices_offset);
void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
{
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);

Tofu Linden's avatar
Tofu Linden committed
	llassert(mRequestedNumVerts >= 0);
	if (first >= (U32) mRequestedNumVerts ||
Tofu Linden's avatar
Tofu Linden committed
	    first + count > (U32) mRequestedNumVerts)
	{
		llerrs << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << llendl;
	}

	if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
	{
		llerrs << "Wrong vertex buffer bound." << llendl;
	}

Tofu Linden's avatar
Tofu Linden committed
	if (mode >= LLRender::NUM_MODES)
	{
		llerrs << "Invalid draw mode: " << mode << llendl;
	glDrawArrays(sGLMode[mode], first, count);
	stop_glerror();
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 ;
Xiaohong Bao's avatar
Xiaohong Bao committed

	if(!sPrivatePoolp)
	{
		sPrivatePoolp = LLPrivateMemoryPoolManager::getInstance()->newPool(LLPrivateMemoryPool::STATIC) ;
	}
}

//static 
void LLVertexBuffer::unbind()
{
	if (sVBOActive)
	{
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
		sVBOActive = FALSE;
	}
	if (sIBOActive)
	{
		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
		sIBOActive = FALSE;
	}

	sGLRenderBuffer = 0;
	sGLRenderIndices = 0;

	setupClientArrays(0);
}

//static
void LLVertexBuffer::cleanupClass()
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
	clientCopy(); // deletes GL buffers
Xiaohong Bao's avatar
Xiaohong Bao committed

Xiaohong Bao's avatar
Xiaohong Bao committed
	if(sPrivatePoolp)
	{
		LLPrivateMemoryPoolManager::getInstance()->deletePool(sPrivatePoolp) ;
		sPrivatePoolp = NULL ;
	}
{
	if (!sDeleteList.empty())
	{
		glDeleteBuffersARB(sDeleteList.size(), (GLuint*) &(sDeleteList[0]));
		sDeleteList.clear();
	}
}

//----------------------------------------------------------------------------

LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
	LLRefCount(),
Tofu Linden's avatar
Tofu Linden committed

	mNumVerts(0),
	mNumIndices(0),
	mRequestedNumVerts(-1),
	mRequestedNumIndices(-1),
	mUsage(usage),
	mGLBuffer(0),
	mGLIndices(0), 
	mMappedIndexData(NULL), 
	mVertexLocked(FALSE),
	mIndexLocked(FALSE),
	LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);

	if (mUsage == GL_STREAM_DRAW_ARB && !sUseStreamDraw)
	{
		mUsage = 0;
	}
	if (mUsage == GL_DYNAMIC_DRAW_ARB && sPreferStreamDraw)
David Parks's avatar
David Parks committed
	{
	//zero out offsets
	for (U32 i = 0; i < TYPE_MAX; i++)
	{
		mOffsets[i] = 0;
	}
David Parks's avatar
David Parks committed
	mAlignedOffset = 0;
David Parks's avatar
David Parks committed
	mAlignedIndexOffset = 0;
David Parks's avatar
David Parks committed

S32 LLVertexBuffer::calcOffsets(const U32& typemask, S32* offsets, S32 num_vertices)
David Parks's avatar
David Parks committed
	S32 offset = 0;
	for (S32 i=0; i<TYPE_MAX; i++)
	{
		U32 mask = 1<<i;
		if (typemask & mask)
		{
David Parks's avatar
David Parks committed
				offsets[i] = offset;
				offset += LLVertexBuffer::sTypeSize[i]*num_vertices;
David Parks's avatar
David Parks committed
				offset = (offset + 0xF) & ~0xF;
David Parks's avatar
David Parks committed
	return offset+16;
}

//static 
S32 LLVertexBuffer::calcVertexSize(const U32& typemask)
	S32 size = 0;
	for (S32 i = 0; i < TYPE_MAX; i++)
	{
		U32 mask = 1<<i;
		if (typemask & mask)
		{
			size += LLVertexBuffer::sTypeSize[i];
David Parks's avatar
David Parks committed
S32 LLVertexBuffer::getSize() const
}

// protected, use unref()
//virtual
LLVertexBuffer::~LLVertexBuffer()
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTRUCTOR);
	destroyGLBuffer();
	destroyGLIndices();
	sCount--;
	llassert_always(!mMappedData && !mMappedIndexData) ;
void LLVertexBuffer::placeFence() const
{
	/*if (!mFence && useVBOs())
	{
		if (gGLManager.mHasSync)
		{
			mFence = new LLGLSyncFence();
		}
	}

	if (mFence)
	{
		mFence->placeFence();
	}*/
}

void LLVertexBuffer::waitFence() const
{
	/*if (mFence)
	{
		mFence->wait();
	}*/
}

//----------------------------------------------------------------------------

void LLVertexBuffer::genBuffer()
{
	if (mUsage == GL_STREAM_DRAW_ARB)
	{
		mGLBuffer = sStreamVBOPool.allocate();
	}
	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
	{
		mGLBuffer = sDynamicVBOPool.allocate();
	}
	else
	{
		BOOST_STATIC_ASSERT(sizeof(mGLBuffer) == sizeof(GLuint));
		glGenBuffersARB(1, (GLuint*)&mGLBuffer);
	}
	sGLCount++;
}

void LLVertexBuffer::genIndices()
{
	if (mUsage == GL_STREAM_DRAW_ARB)
	{
		mGLIndices = sStreamIBOPool.allocate();
	}
	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
	{
		mGLIndices = sDynamicIBOPool.allocate();
	}
	else
	{
		BOOST_STATIC_ASSERT(sizeof(mGLBuffer) == sizeof(GLuint));
		glGenBuffersARB(1, (GLuint*)&mGLIndices);
	}
	sGLCount++;
}

void LLVertexBuffer::releaseBuffer()
{
	if (mUsage == GL_STREAM_DRAW_ARB)
	{
		sStreamVBOPool.release(mGLBuffer);
	}
	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
	{
		sDynamicVBOPool.release(mGLBuffer);
	}
	else
	{
		sDeleteList.push_back(mGLBuffer);
	}
	sGLCount--;
}

void LLVertexBuffer::releaseIndices()
{
	if (mUsage == GL_STREAM_DRAW_ARB)
	{
		sStreamIBOPool.release(mGLIndices);
	}
	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
	{
		sDynamicIBOPool.release(mGLIndices);
	}
	else
	{
		sDeleteList.push_back(mGLIndices);
	}
	sGLCount--;
}

void LLVertexBuffer::createGLBuffer()
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES);
	
	U32 size = getSize();
	if (mGLBuffer)
	{
		destroyGLBuffer();
	}

	if (size == 0)
	{
		return;
	}

	mEmpty = TRUE;

	if (useVBOs())
	{
		mResized = TRUE;
	}
	else
	{
		static int gl_buffer_idx = 0;
		mGLBuffer = ++gl_buffer_idx;
		mMappedData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
	}
}

void LLVertexBuffer::createGLIndices()
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES);
	U32 size = getIndicesSize();

	if (mGLIndices)
	{
		destroyGLIndices();
	}
	
	if (size == 0)
	{
		return;
	}

	mEmpty = TRUE;

David Parks's avatar
David Parks committed
	//pad by 16 bytes for aligned copies
	size += 16;

David Parks's avatar
David Parks committed
		//pad by another 16 bytes for VBO pointer adjustment
		size += 16;
		mMappedIndexData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
		static int gl_buffer_idx = 0;
		mGLIndices = ++gl_buffer_idx;
	}
}

void LLVertexBuffer::destroyGLBuffer()
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_BUFFER);
	if (mGLBuffer)
	{
		if (useVBOs())
		{
			if (mMappedData || mMappedIndexData)
			{
				llerrs << "Vertex buffer destroyed while mapped!" << llendl;
			}
			releaseBuffer();
		}
		else
		{
			FREE_MEM(sPrivatePoolp, mMappedData) ;
		sAllocatedBytes -= getSize();
	}
	
	mGLBuffer = 0;
}

void LLVertexBuffer::destroyGLIndices()
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_INDICES);
	if (mGLIndices)
	{
		if (useVBOs())
		{
			if (mMappedData || mMappedIndexData)
			{
				llerrs << "Vertex buffer destroyed while mapped." << llendl;
			}
			FREE_MEM(sPrivatePoolp, mMappedIndexData) ;
		sAllocatedBytes -= getIndicesSize();
	}

	mGLIndices = 0;
}

void LLVertexBuffer::updateNumVerts(S32 nverts)
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_VERTS);
Tofu Linden's avatar
Tofu Linden committed
	llassert(nverts >= 0);

	if (nverts >= 65535)
	{
		llwarns << "Vertex buffer overflow!" << llendl;
		nverts = 65535;
	}

	mRequestedNumVerts = nverts;
David Parks's avatar
David Parks committed

	if (!mDynamicSize)
	{
		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;
	}
	mSize = calcOffsets(mTypeMask, mOffsets, mNumVerts);
}

void LLVertexBuffer::updateNumIndices(S32 nindices)
{
	LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_INDICES);
Tofu Linden's avatar
Tofu Linden committed

	llassert(nindices >= 0);

	if (!mDynamicSize)
	{
		mNumIndices = nindices;