Skip to content
Snippets Groups Projects
llvertexbuffer.cpp 59.5 KiB
Newer Older
David Parks's avatar
David Parks committed
	S32 offset = 0;
	for (S32 i=0; i<TYPE_TEXTURE_INDEX; i++)
	{
		U32 mask = 1<<i;
		if (typemask & mask)
		{
			if (offsets && LLVertexBuffer::sTypeSize[i])
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;
	offsets[TYPE_TEXTURE_INDEX] = offsets[TYPE_VERTEX] + 12;
	
David Parks's avatar
David Parks committed
	return offset+16;
}

//static 
S32 LLVertexBuffer::calcVertexSize(const U32& typemask)
	for (S32 i = 0; i < TYPE_TEXTURE_INDEX; 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()
{
	destroyGLBuffer();
	destroyGLIndices();
David Parks's avatar
David Parks committed
#if GL_ARB_vertex_array_object
David Parks's avatar
David Parks committed
#endif
	sVertexCount -= mNumVerts;
	sIndexCount -= mNumIndices;

	if (mMappedData)
	{
		LL_ERRS() << "Failed to clear vertex buffer's vertices" << LL_ENDL;
	}
	if (mMappedIndexData)
	{
		LL_ERRS() << "Failed to clear vertex buffer's indices" << LL_ENDL;
	}
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(U32 size)
		mMappedData = sStreamVBOPool.allocate(mGLBuffer, mSize);
	else if (mUsage == GL_DYNAMIC_DRAW_ARB)
		mMappedData = sDynamicVBOPool.allocate(mGLBuffer, mSize);
	else
	{
		mMappedData = sDynamicCopyVBOPool.allocate(mGLBuffer, mSize);
	}
void LLVertexBuffer::genIndices(U32 size)
	mIndicesSize = vbo_block_size(size);
		mMappedIndexData = sStreamIBOPool.allocate(mGLIndices, mIndicesSize);
		mMappedIndexData = sDynamicIBOPool.allocate(mGLIndices, mIndicesSize);
	sGLCount++;
}

void LLVertexBuffer::releaseBuffer()
{
	if (mUsage == GL_STREAM_DRAW_ARB)
	{
		sStreamVBOPool.release(mGLBuffer, mMappedData, mSize);
		sDynamicVBOPool.release(mGLBuffer, mMappedData, mSize);
	sGLCount--;
}

void LLVertexBuffer::releaseIndices()
{
	if (mUsage == GL_STREAM_DRAW_ARB)
	{
		sStreamIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
		sDynamicIBOPool.release(mGLIndices, mMappedIndexData, mIndicesSize);
bool LLVertexBuffer::createGLBuffer(U32 size)
{
	if (mGLBuffer)
	{
		destroyGLBuffer();
	}

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

	if (!mMappedData)
	{
		sucsess = false;
	}
	return sucsess;
bool LLVertexBuffer::createGLIndices(U32 size)
{
	if (mGLIndices)
	{
		destroyGLIndices();
	}
	
	if (size == 0)
	{
David Parks's avatar
David Parks committed
	//pad by 16 bytes for aligned copies
	size += 16;

	mMappedIndexDataUsingVBOs = useVBOs();

	if (mMappedIndexDataUsingVBOs)
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;

	if (!mMappedIndexData)
	{
		sucsess = false;
	}
	return sucsess;
}

void LLVertexBuffer::destroyGLBuffer()
{
			FREE_MEM(sPrivatePoolp, (void*) mMappedData);
}

void LLVertexBuffer::destroyGLIndices()
{
			FREE_MEM(sPrivatePoolp, (void*) mMappedIndexData);
bool LLVertexBuffer::updateNumVerts(S32 nverts)
Tofu Linden's avatar
Tofu Linden committed
	llassert(nverts >= 0);

	if (nverts > 65536)
		LL_WARNS() << "Vertex buffer overflow!" << LL_ENDL;
	U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts);
David Parks's avatar
David Parks committed

	if (needed_size > mSize || needed_size <= mSize/2)
		sucsess &= createGLBuffer(needed_size);
bool LLVertexBuffer::updateNumIndices(S32 nindices)
Tofu Linden's avatar
Tofu Linden committed
	llassert(nindices >= 0);

	U32 needed_size = sizeof(U16) * nindices;

	if (needed_size > mIndicesSize || needed_size <= mIndicesSize/2)
		sucsess &= createGLIndices(needed_size);
bool LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
David Parks's avatar
David Parks committed
	if (nverts < 0 || nindices < 0 ||
		nverts > 65536)
	{
		LL_ERRS() << "Bad vertex buffer allocation: " << nverts << " : " << nindices << LL_ENDL;
	bool sucsess = true;

	sucsess &= updateNumVerts(nverts);
	sucsess &= updateNumIndices(nindices);
	if (create && (nverts || nindices))
		//actually allocate space for the vertex buffer if using VBO mapping
		if (gGLManager.mHasVertexArrayObject && useVBOs() && sUseVAO)
David Parks's avatar
David Parks committed
#if GL_ARB_vertex_array_object
David Parks's avatar
David Parks committed
#endif
static LLTrace::BlockTimerStatHandle FTM_SETUP_VERTEX_ARRAY("Setup VAO");
	LL_RECORD_BLOCK_TIME(FTM_SETUP_VERTEX_ARRAY);
#if GL_ARB_vertex_array_object
	glBindVertexArray(mGLArray);
#endif
	sGLRenderArray = mGLArray;

	U32 attrib_size[] = 
	{
		3, //TYPE_VERTEX,
		3, //TYPE_NORMAL,
		2, //TYPE_TEXCOORD0,
		2, //TYPE_TEXCOORD1,
		2, //TYPE_TEXCOORD2,
		2, //TYPE_TEXCOORD3,
		4, //TYPE_COLOR,
		1, //TYPE_WEIGHT,
		4, //TYPE_WEIGHT4,
		4, //TYPE_CLOTHWEIGHT,
	};

	U32 attrib_type[] =
	{
		GL_FLOAT, //TYPE_VERTEX,
		GL_FLOAT, //TYPE_NORMAL,
		GL_FLOAT, //TYPE_TEXCOORD0,
		GL_FLOAT, //TYPE_TEXCOORD1,
		GL_FLOAT, //TYPE_TEXCOORD2,
		GL_FLOAT, //TYPE_TEXCOORD3,
		GL_UNSIGNED_BYTE, //TYPE_COLOR,
		GL_UNSIGNED_BYTE, //TYPE_EMISSIVE,
		GL_FLOAT, //TYPE_WEIGHT,
		GL_FLOAT, //TYPE_WEIGHT4,
		GL_FLOAT, //TYPE_CLOTHWEIGHT,
	};

	bool attrib_integer[] = 
	{
		false, //TYPE_VERTEX,
		false, //TYPE_NORMAL,
		false, //TYPE_TEXCOORD0,
		false, //TYPE_TEXCOORD1,
		false, //TYPE_TEXCOORD2,
		false, //TYPE_TEXCOORD3,
		false, //TYPE_COLOR,
		false, //TYPE_EMISSIVE,
		false, //TYPE_WEIGHT,
		false, //TYPE_WEIGHT4,
		false, //TYPE_CLOTHWEIGHT,
		true, //TYPE_TEXTURE_INDEX
	};

	U32 attrib_normalized[] =
	{
		GL_FALSE, //TYPE_VERTEX,
		GL_FALSE, //TYPE_NORMAL,
		GL_FALSE, //TYPE_TEXCOORD0,
		GL_FALSE, //TYPE_TEXCOORD1,
		GL_FALSE, //TYPE_TEXCOORD2,
		GL_FALSE, //TYPE_TEXCOORD3,
		GL_TRUE, //TYPE_COLOR,
		GL_TRUE, //TYPE_EMISSIVE,
		GL_FALSE, //TYPE_WEIGHT,
		GL_FALSE, //TYPE_WEIGHT4,
		GL_FALSE, //TYPE_CLOTHWEIGHT,
	};

	bindGLBuffer(true);
	bindGLIndices(true);

	for (U32 i = 0; i < TYPE_MAX; ++i)
	{
		if (mTypeMask & (1 << i))
		{
			glEnableVertexAttribArrayARB(i);
			if (attrib_integer[i])
David Parks's avatar
David Parks committed
#if !LL_DARWIN
				//glVertexattribIPointer requires GLSL 1.30 or later
				if (gGLManager.mGLSLVersionMajor > 1 || gGLManager.mGLSLVersionMinor >= 30)
				{
					glVertexAttribIPointer(i, attrib_size[i], attrib_type[i], sTypeSize[i], (const GLvoid*) mOffsets[i]); 
David Parks's avatar
David Parks committed
#endif
				// nat 2016-12-16: With 64-bit clang compile, the compiler
				// produces an error if we simply cast mOffsets[i] -- an S32
				// -- to (GLvoid *), the type of the parameter. It correctly
				// points out that there's no way an S32 could fit a real
				// pointer value. Ruslan asserts that in this case the last
				// param is interpreted as an array data offset within the VBO
				// rather than as an actual pointer, so it's okay.
				glVertexAttribPointerARB(i, attrib_size[i], attrib_type[i],
										 attrib_normalized[i], sTypeSize[i],
										 reinterpret_cast<GLvoid*>(mOffsets[i])); 
	//draw a dummy triangle to set index array pointer
	//glDrawElements(GL_TRIANGLES, 0, GL_UNSIGNED_SHORT, NULL);

bool LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
Tofu Linden's avatar
Tofu Linden committed
	llassert(newnverts >= 0);
	llassert(newnindices >= 0);

	bool sucsess = true;

	sucsess &= updateNumVerts(newnverts);		
	sucsess &= updateNumIndices(newnindices);

		if (mGLArray)
		{ //if size changed, offsets changed
			setupVertexArray();
		}
	//it's generally ineffective to use VBO for things that are streaming on apple
}

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

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

static LLTrace::BlockTimerStatHandle FTM_VBO_MAP_BUFFER_RANGE("VBO Map Range");
static LLTrace::BlockTimerStatHandle FTM_VBO_MAP_BUFFER("VBO Map");
// Map for data access
volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
		LL_ERRS() << "LLVertexBuffer::mapVeretxBuffer() called on a finalized buffer." << LL_ENDL;
	if (!useVBOs() && !mMappedData && !mMappedIndexData)
		LL_ERRS() << "LLVertexBuffer::mapVertexBuffer() called on unallocated buffer." << LL_ENDL;
		if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
			}

			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;
				MappedRegion region(type, mMappable && map_range ? -1 : index, count);
				mMappedVertexRegions.push_back(region);
			}
		}

		if (mVertexLocked && map_range)
		{
			LL_ERRS() << "Attempted to map a specific range of a buffer that was already mapped." << LL_ENDL;
				if (gGLManager.mHasMapBufferRange)
				{
					if (map_range)
					{
#ifdef GL_ARB_map_buffer_range
						LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_BUFFER_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 | 
#ifdef GL_ARB_map_buffer_range

						if (gDebugGL)
						{
							GLint size = 0;
							glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size);

							if (size < mSize)
							{
								LL_ERRS() << "Invalid buffer size." << LL_ENDL;
						LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_BUFFER);
						src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, 0, mSize, 
							GL_MAP_WRITE_BIT | 
#endif
					}
				}
				else if (gGLManager.mHasFlushBufferRange)
				{
					if (map_range)
					{
						glBufferParameteriAPPLE(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
						glBufferParameteriAPPLE(GL_ARRAY_BUFFER_ARB, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
						src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
					}
					else
					{
						src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
					}
				}
				else
				{
					map_range = false;
					src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
				}

				llassert(src != NULL);

				mMappedData = LL_NEXT_ALIGNED_ADDRESS<volatile U8>(src);
Xiaohong Bao's avatar
Xiaohong Bao committed
				mAlignedOffset = mMappedData - src;
Xiaohong Bao's avatar
Xiaohong Bao committed
				stop_glerror();
				LLMemory::logMemoryInfo(true);
				{			
					//--------------------
					//print out more debug info before crash
					LL_INFOS() << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << LL_ENDL;
					GLint size;
					glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size);
					LL_INFOS() << "GL_ARRAY_BUFFER_ARB size is " << size << LL_ENDL;
					//--------------------

					GLint buff;
					glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff);
					if ((GLuint)buff != mGLBuffer)
					{
						LL_ERRS() << "Invalid GL vertex buffer bound: " << buff << LL_ENDL;
					LL_ERRS() << "glMapBuffer returned NULL (no vertex data)" << LL_ENDL;
					LL_ERRS() << "memory allocation for vertex data failed." << LL_ENDL;
	if (map_range && gGLManager.mHasMapBufferRange && mMappable)
	{
		return mMappedData;
	}
	else
	{
		return mMappedData+mOffsets[type]+sTypeSize[type]*index;
	}
static LLTrace::BlockTimerStatHandle FTM_VBO_MAP_INDEX_RANGE("IBO Map Range");
static LLTrace::BlockTimerStatHandle FTM_VBO_MAP_INDEX("IBO Map");

volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
		LL_ERRS() << "LLVertexBuffer::mapIndexBuffer() called on a finalized buffer." << LL_ENDL;
		LL_ERRS() << "LLVertexBuffer::mapIndexBuffer() called on unallocated buffer." << LL_ENDL;
		if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
			}

			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;
				MappedRegion region(TYPE_INDEX, mMappable && map_range ? -1 : index, count);
				mMappedIndexRegions.push_back(region);
			}
		}

		if (mIndexLocked && map_range)
		{
			LL_ERRS() << "Attempted to map a specific range of a buffer that was already mapped." << LL_ENDL;
			if (gDebugGL && useVBOs())
			{
				GLint elem = 0;
				glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &elem);

				if (elem != mGLIndices)
				{
					LL_ERRS() << "Wrong index buffer bound!" << LL_ENDL;
				if (gGLManager.mHasMapBufferRange)
				{
					if (map_range)
					{
#ifdef GL_ARB_map_buffer_range
						LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_INDEX_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 | 
#ifdef GL_ARB_map_buffer_range
						LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_INDEX);
						src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, sizeof(U16)*mNumIndices, 
							GL_MAP_WRITE_BIT | 
#endif
					}
				}
				else if (gGLManager.mHasFlushBufferRange)
				{
					if (map_range)
					{
						glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
						glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
						src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
						src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
					LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_INDEX);
					map_range = false;
					src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
				}

				llassert(src != NULL);


				mMappedIndexData = src; //LL_NEXT_ALIGNED_ADDRESS<U8>(src);
Xiaohong Bao's avatar
Xiaohong Bao committed
				mAlignedIndexOffset = mMappedIndexData - src;
				stop_glerror();

		if (!mMappedIndexData)
		{
			LLMemory::logMemoryInfo(true);
				GLint buff;
				glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff);
				if ((GLuint)buff != mGLIndices)
				{
					LL_ERRS() << "Invalid GL index buffer bound: " << buff << LL_ENDL;
				LL_ERRS() << "glMapBuffer returned NULL (no index data)" << LL_ENDL;
				LL_ERRS() << "memory allocation for Index data failed. " << LL_ENDL;
	if (map_range && gGLManager.mHasMapBufferRange && mMappable)
	{
		return mMappedIndexData;
	}
	else
	{
		return mMappedIndexData + sizeof(U16)*index;
	}
static LLTrace::BlockTimerStatHandle FTM_VBO_UNMAP("VBO Unmap");
static LLTrace::BlockTimerStatHandle FTM_VBO_FLUSH_RANGE("Flush VBO Range");
static LLTrace::BlockTimerStatHandle FTM_IBO_UNMAP("IBO Unmap");
static LLTrace::BlockTimerStatHandle FTM_IBO_FLUSH_RANGE("Flush IBO Range");
		return; //nothing to unmap
		LL_RECORD_BLOCK_TIME(FTM_VBO_UNMAP);
		bindGLBuffer(true);
		updated_all = mIndexLocked; //both vertex and index buffers done updating
			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;
					glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, offset, length, (U8*) mMappedData+offset);
				glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), (U8*) mMappedData);
			if (gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
			{
				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;
						if (gGLManager.mHasMapBufferRange)
						{
							LL_RECORD_BLOCK_TIME(FTM_VBO_FLUSH_RANGE);
#ifdef GL_ARB_map_buffer_range
							glFlushMappedBufferRange(GL_ARRAY_BUFFER_ARB, offset, length);
#endif
						}
						else if (gGLManager.mHasFlushBufferRange)
						{
							glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER_ARB, offset, length);
			stop_glerror();
			glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
			stop_glerror();
		LL_RECORD_BLOCK_TIME(FTM_IBO_UNMAP);
			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;