Skip to content
Snippets Groups Projects
llvertexbuffer.cpp 61.6 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 "llfasttimer.h"
#include "llvertexbuffer.h"
// #include "llrender.h"
#include "llglheaders.h"
#include "llrender.h"
David Parks's avatar
David Parks committed
#include "llvector4a.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;
}

//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;
}


const U32 LL_VBO_BLOCK_SIZE = 2048;
const U32 LL_VBO_POOL_MAX_SEED_SIZE = 256*1024;

U32 vbo_block_size(U32 size)
{ //what block size will fit size?
	U32 mod = size % LL_VBO_BLOCK_SIZE;
	return mod == 0 ? size : size + (LL_VBO_BLOCK_SIZE-mod);
}

U32 vbo_block_index(U32 size)
{
	return vbo_block_size(size)/LL_VBO_BLOCK_SIZE;
}

const U32 LL_VBO_POOL_SEED_COUNT = vbo_block_index(LL_VBO_POOL_MAX_SEED_SIZE);

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

//static
LLVBOPool LLVertexBuffer::sStreamVBOPool(GL_STREAM_DRAW, GL_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sDynamicVBOPool(GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sDynamicCopyVBOPool(GL_DYNAMIC_COPY, GL_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sStreamIBOPool(GL_STREAM_DRAW, GL_ELEMENT_ARRAY_BUFFER);
LLVBOPool LLVertexBuffer::sDynamicIBOPool(GL_DYNAMIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
U32 LLVBOPool::sBytesPooled = 0;
U32 LLVBOPool::sIndexBytesPooled = 0;

std::list<U32> LLVertexBuffer::sAvailableVAOName;
U32 LLVertexBuffer::sCurVAOName = 1;

U32 LLVertexBuffer::sAllocatedIndexBytes = 0;
U32 LLVertexBuffer::sIndexCount = 0;

U32 LLVertexBuffer::sBindCount = 0;
U32 LLVertexBuffer::sSetCount = 0;
S32 LLVertexBuffer::sCount = 0;
S32 LLVertexBuffer::sGLCount = 0;
S32 LLVertexBuffer::sMappedCount = 0;
David Parks's avatar
David Parks committed
bool LLVertexBuffer::sDisableVBOMapping = false;
bool LLVertexBuffer::sEnableVBOs = true;
U32 LLVertexBuffer::sGLRenderArray = 0;
U32 LLVertexBuffer::sLastMask = 0;
David Parks's avatar
David Parks committed
bool LLVertexBuffer::sVBOActive = false;
bool LLVertexBuffer::sIBOActive = false;
U32 LLVertexBuffer::sAllocatedBytes = 0;
U32 LLVertexBuffer::sVertexCount = 0;
David Parks's avatar
David Parks committed
bool LLVertexBuffer::sMapped = false;
bool LLVertexBuffer::sUseStreamDraw = true;
bool LLVertexBuffer::sUseVAO = false;
bool LLVertexBuffer::sPreferStreamDraw = false;
	if (gGLManager.mInited)
	{
		LLVertexBuffer::unbind();
		glBindBuffer(mType, name);
		glBufferData(mType, 0, NULL, mUsage);
		glBindBuffer(mType, 0);
LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType)
: mUsage(vboUsage), mType(vboType)
{
	mMissCount.resize(LL_VBO_POOL_SEED_COUNT);
	std::fill(mMissCount.begin(), mMissCount.end(), 0);
}

volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed)
	llassert(vbo_block_size(size) == size);
	
	volatile U8* ret = NULL;

	U32 i = vbo_block_index(size);
		if (!for_seed && i < LL_VBO_POOL_SEED_COUNT)
		{ //record this miss
			mMissCount[i]++;	
		}

		{
			LLVertexBuffer::sAllocatedBytes += size;
		}
		else
		{
			LLVertexBuffer::sAllocatedIndexBytes += size;
		}
		if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW)
			glBufferData(mType, size, 0, mUsage);
			if (mUsage != GL_DYNAMIC_COPY)
			{ //data will be provided by application
				ret = (U8*) ll_aligned_malloc<64>(size);
					LL_ERRS() << "Failed to allocate "<< size << " bytes for LLVBOPool buffer " << name <<"." << LL_NEWLINE
							  << "Free list size: " << mFreeList.size() // this happens if we are out of memory so a solution might be to clear some from freelist
							  << " Allocated Bytes: " << LLVertexBuffer::sAllocatedBytes
							  << " Allocated Index Bytes: " << LLVertexBuffer::sAllocatedIndexBytes
							  << " Pooled Bytes: " << sBytesPooled
							  << " Pooled Index Bytes: " << sIndexBytesPooled
							  << LL_ENDL;
		else
		{ //always use a true hint of static draw when allocating non-client-backed buffers
			glBufferData(mType, size, nullptr, GL_STATIC_DRAW);

		if (for_seed)
		{ //put into pool for future use
			llassert(mFreeList.size() > i);

			Record rec;
			rec.mGLName = name;
			rec.mClientData = ret;
	
			{
				sBytesPooled += size;
			}
			else
			{
				sIndexBytesPooled += size;
			}
			mFreeList[i].push_back(rec);
		}
	}
	else
	{
		name = mFreeList[i].front().mGLName;
		ret = mFreeList[i].front().mClientData;

		{
			sBytesPooled -= size;
		}
		else
		{
			sIndexBytesPooled -= size;
		}
void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
	llassert(vbo_block_size(size) == size);
	ll_aligned_free<64>((U8*) buffer);
	{
		LLVertexBuffer::sAllocatedBytes -= size;
	}
	else
	{
		LLVertexBuffer::sAllocatedIndexBytes -= size;
	}
void LLVBOPool::seedPool()
{
	U32 dummy_name = 0;

	if (mFreeList.size() < LL_VBO_POOL_SEED_COUNT)
	{
		mFreeList.resize(LL_VBO_POOL_SEED_COUNT);
	}

	for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++)
	{
		if (mMissCount[i] > mFreeList[i].size())
		{ 
			U32 size = i*LL_VBO_BLOCK_SIZE;
		
			S32 count = mMissCount[i] - mFreeList[i].size();
			for (U32 j = 0; j < count; ++j)
			{
				allocate(dummy_name, size, true);
			}
		}
	}
}



	for (U32 i = 0; i < mFreeList.size(); ++i)
	{
		record_list_t& l = mFreeList[i];

		while (!l.empty())
		{
			Record& r = l.front();

				ll_aligned_free<64>((void*) r.mClientData);
			{
				sBytesPooled -= size;
				LLVertexBuffer::sAllocatedBytes -= size;
			}
			else
			{
				sIndexBytesPooled -= size;
				LLVertexBuffer::sAllocatedIndexBytes -= size;
			}

	//reset miss counts
	std::fill(mMissCount.begin(), mMissCount.end(), 0);
//NOTE: each component must be AT LEAST 4 bytes in size to avoid a performance penalty on AMD hardware
ruslantproductengine's avatar
ruslantproductengine committed
const 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(LLColor4U), // TYPE_EMISSIVE, only alpha is used currently
	sizeof(F32),	   // TYPE_WEIGHT,
David Parks's avatar
David Parks committed
	sizeof(LLVector4), // TYPE_WEIGHT4,
	sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
	sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
ruslantproductengine's avatar
ruslantproductengine committed
static const std::string vb_type_name[] =
{
	"TYPE_VERTEX",
	"TYPE_NORMAL",
	"TYPE_TEXCOORD0",
	"TYPE_TEXCOORD1",
	"TYPE_TEXCOORD2",
	"TYPE_TEXCOORD3",
	"TYPE_COLOR",
	"TYPE_EMISSIVE",
	"TYPE_WEIGHT",
	"TYPE_WEIGHT4",
	"TYPE_CLOTHWEIGHT",
	"TYPE_TEXTURE_INDEX",
	"TYPE_MAX",
	"TYPE_INDEX",	
};

ruslantproductengine's avatar
ruslantproductengine committed
const 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
U32 LLVertexBuffer::getVAOName()
{
	U32 ret = 0;

	if (!sAvailableVAOName.empty())
	{
		ret = sAvailableVAOName.front();
		sAvailableVAOName.pop_front();
	}
	else
	{
#ifdef GL_ARB_vertex_array_object
#endif
	}

	return ret;		
}

//static
void LLVertexBuffer::releaseVAOName(U32 name)
{
	sAvailableVAOName.push_back(name);
}

//static
void LLVertexBuffer::seedPools()
{
	sStreamVBOPool.seedPool();
	sDynamicVBOPool.seedPool();
	sDynamicCopyVBOPool.seedPool();
	sStreamIBOPool.seedPool();
	sDynamicIBOPool.seedPool();
}

void LLVertexBuffer::setupClientArrays(U32 data_mask)
		if (gGLManager.mGLSLVersionMajor < 2 && gGLManager.mGLSLVersionMinor < 30)
		{
			//make sure texture index is disabled
			data_mask = data_mask & ~MAP_TEXTURE_INDEX;
		}

		if (LLGLSLShader::sNoFixedFunction)
		{
			for (U32 i = 0; i < TYPE_MAX; ++i)
				S32 loc = i;
										
				U32 mask = 1 << i;

				if (sLastMask & (1 << i))
				{ //was enabled
					if (!(data_mask & mask))
					{ //needs to be disabled
				}
				else 
				{	//was disabled
					if (data_mask & mask)
					{ //needs to be enabled
			static const GLenum array[] =
			{
				GL_VERTEX_ARRAY,
				GL_NORMAL_ARRAY,
				GL_TEXTURE_COORD_ARRAY,
				GL_COLOR_ARRAY,
			};

			static const GLenum mask[] = 
			{
				MAP_VERTEX,
				MAP_NORMAL,
				MAP_TEXCOORD0,
				MAP_COLOR
			};



			for (U32 i = 0; i < 4; ++i)
			{
				if (sLastMask & mask[i])
				{ //was enabled
					if (!(data_mask & mask[i]))
					{ //needs to be disabled
						glDisableClientState(array[i]);
					}
					else if (gDebugGL)
					{ //needs to be enabled, make sure it was (DEBUG)
						if (!glIsEnabled(array[i]))
						{
							if (gDebugSession)
							{
								gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl;
							}
							else
							{
								LL_ERRS() << "Bad client state! " << array[i] << " disabled." << LL_ENDL;
				else 
				{	//was disabled
					if (data_mask & mask[i])
					{ //needs to be enabled
						glEnableClientState(array[i]);
					}
					else if (gDebugGL && glIsEnabled(array[i]))
					{ //needs to be disabled, make sure it was (DEBUG TEMPORARY)
							gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl;
							LL_ERRS() << "Bad client state! " << array[i] << " enabled." << LL_ENDL;
			static const U32 map_tc[] = 
				if (sLastMask & map_tc[i])
				{
					if (!(data_mask & map_tc[i]))
					{ //disable
						glClientActiveTexture(GL_TEXTURE1+i);
						glDisableClientState(GL_TEXTURE_COORD_ARRAY);
					glClientActiveTexture(GL_TEXTURE1+i);
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
static LLTrace::BlockTimerStatHandle FTM_VB_DRAW_ARRAYS("drawArrays");
void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
	//LL_RECORD_BLOCK_TIME(FTM_VB_DRAW_ARRAYS);
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
	U32 count = pos.size();
	
	llassert(norm.size() >= pos.size());
	llassert(count > 0);

	if( count == 0 )
	{
		LL_WARNS() << "Called drawArrays with 0 vertices" << LL_ENDL;
		LL_WARNS() << "Called drawArrays with #" << norm.size() << " normals and #" << pos.size() << " vertices" << LL_ENDL;

	unbind();
	
	setupClientArrays(MAP_VERTEX | MAP_NORMAL);

	LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;

	if (shader)
	{
			glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
			glVertexAttribPointer(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);
	LLGLSLShader::stopProfile(count, mode);
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);

		glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
			glVertexAttribPointer(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);
	LLGLSLShader::stopProfile(num_indices, mode);
void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
    llassert(start < (U32)mNumVerts);
    llassert(end < (U32)mNumVerts);

	if (start >= (U32) mNumVerts ||
	    end >= (U32) mNumVerts)
		LL_ERRS() << "Bad vertex buffer draw range: [" << start << ", " << end << "] vs " << mNumVerts << LL_ENDL;
Tofu Linden's avatar
Tofu Linden committed

	if (indices_offset >= (U32) mNumIndices ||
	    indices_offset + count > (U32) mNumIndices)
		LL_ERRS() << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << LL_ENDL;
	if (gDebugGL && !useVBOs())
	{
		U16* idx = ((U16*) getIndicesPointer())+indices_offset;
		for (U32 i = 0; i < count; ++i)
		{
            llassert(idx[i] >= start);
            llassert(idx[i] <= end);

			if (idx[i] < start || idx[i] > end)
			{
				LL_ERRS() << "Index out of range: " << idx[i] << " not in [" << start << ", " << end << "]" << LL_ENDL;

		LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;

		if (shader && shader->mFeatures.mIndexedTextureChannels > 1)
		{
			LLStrider<LLVector4a> v;
			//hack to get non-const reference
			LLVertexBuffer* vb = (LLVertexBuffer*) this;
			vb->getVertexStrider(v);

			for (U32 i = start; i < end; i++)
			{
				S32 idx = (S32) (v[i][3]+0.25f);
				if (idx < 0 || idx >= shader->mFeatures.mIndexedTextureChannels)
				{
					LL_ERRS() << "Bad texture index found in vertex data stream." << LL_ENDL;
	}
}

void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
{
	validateRange(start, end, count, indices_offset);
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
			LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
			LL_ERRS() << "Wrong index buffer bound." << LL_ENDL;
			LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
	if (gDebugGL && !mGLArray && useVBOs())
		glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elem);
			LL_ERRS() << "Wrong index buffer bound!" << LL_ENDL;
Tofu Linden's avatar
Tofu Linden committed
	if (mode >= LLRender::NUM_MODES)
		LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
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);
	LLGLSLShader::stopProfile(count, mode);
	stop_glerror();
}

void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
{
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
	llassert(mNumIndices >= 0);
	if (indices_offset >= (U32) mNumIndices ||
	    indices_offset + count > (U32) mNumIndices)
		LL_ERRS() << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << LL_ENDL;
			LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
			LL_ERRS() << "Wrong index buffer bound." << LL_ENDL;
			LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
Tofu Linden's avatar
Tofu Linden committed
	if (mode >= LLRender::NUM_MODES)
		LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
	glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT,
		((U16*) getIndicesPointer()) + indices_offset);
	LLGLSLShader::stopProfile(count, mode);
static LLTrace::BlockTimerStatHandle FTM_GL_DRAW_ARRAYS("GL draw arrays");
void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
{
	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
	llassert(mNumVerts >= 0);
	if (first >= (U32) mNumVerts ||
	    first + count > (U32) mNumVerts)
		LL_ERRS() << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << LL_ENDL;
			LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
		}
	}
	else
	{
		if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
		{
			LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
Tofu Linden's avatar
Tofu Linden committed
	if (mode >= LLRender::NUM_MODES)
		LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
		//LL_RECORD_BLOCK_TIME(FTM_GL_DRAW_ARRAYS);
		LLGLSLShader::startProfile();
		stop_glerror();
		glDrawArrays(sGLMode[mode], first, count);
		stop_glerror();
		LLGLSLShader::stopProfile(count, mode);
	stop_glerror();
void LLVertexBuffer::initClass(bool use_vbo, bool no_vbo_mapping)
	sEnableVBOs = use_vbo && gGLManager.mHasVertexBufferObject;
	sDisableVBOMapping = sEnableVBOs && no_vbo_mapping;
}

//static 
void LLVertexBuffer::unbind()
{
David Parks's avatar
David Parks committed
#if GL_ARB_vertex_array_object
David Parks's avatar
David Parks committed
#endif
David Parks's avatar
David Parks committed
		sIBOActive = false;
David Parks's avatar
David Parks committed
		sVBOActive = false;
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
David Parks's avatar
David Parks committed
		sIBOActive = false;
	}

	sGLRenderBuffer = 0;
	sGLRenderIndices = 0;

	setupClientArrays(0);
}

//static
void LLVertexBuffer::cleanupClass()
{
	
	sStreamIBOPool.cleanup();
	sDynamicIBOPool.cleanup();
	sStreamVBOPool.cleanup();
	sDynamicVBOPool.cleanup();
	sDynamicCopyVBOPool.cleanup();
}

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

S32 LLVertexBuffer::determineUsage(S32 usage)
	if (ret_usage == GL_STREAM_DRAW && !sUseStreamDraw)
	if (ret_usage == GL_DYNAMIC_DRAW && sPreferStreamDraw)
David Parks's avatar
David Parks committed
	{
	if (ret_usage == 0 && LLRender::sGLCoreProfile)
	if (ret_usage && ret_usage != GL_STREAM_DRAW)
	{ //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
Graham Linden's avatar
Graham Linden committed
		    if (sDisableVBOMapping)
		    { //always use stream draw if VBO mapping is disabled
Graham Linden's avatar
Graham Linden committed
		    }
		    else
		    {
Graham Linden's avatar
Graham Linden committed
		    }
	    }
LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) 
:	LLTrace::MemTrackable<LLVertexBuffer>("LLVertexBuffer"),
	LLRefCount(),

	mNumVerts(0),
	mNumIndices(0),
	mAlignedOffset(0),
	mAlignedIndexOffset(0),
	mSize(0),
	mIndicesSize(0),
	mTypeMask(typemask),
	mUsage(LLVertexBuffer::determineUsage(usage)),
	mGLBuffer(0),
	mGLIndices(0),
	mGLArray(0),
	mMappedData(NULL),
	mMappedIndexData(NULL),
	mMappedDataUsingVBOs(false),
	mMappedIndexDataUsingVBOs(false),
	mVertexLocked(false),
	mIndexLocked(false),
	mFinal(false),
	mEmpty(true),
	mMappable(false),
	mFence(NULL)
{
	mMappable = (mUsage == GL_DYNAMIC_DRAW && !sDisableVBOMapping);
	//zero out offsets
	for (U32 i = 0; i < TYPE_MAX; i++)
	{
		mOffsets[i] = 0;
	}