Skip to content
Snippets Groups Projects
llvertexbuffer.cpp 61.6 KiB
Newer Older
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_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
		// Sanity check. We have weird crashes in this destructor (on delete). Yet mFence is disabled.
		// TODO: mFence was added in scope of SH-2038, but was never enabled, consider removing mFence.
		LL_ERRS() << "LLVertexBuffer destruction failed" << LL_ENDL;
	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);
		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);
		sStreamVBOPool.release(mGLBuffer, mMappedData, mSize);
		sDynamicVBOPool.release(mGLBuffer, mMappedData, mSize);
		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*)ll_aligned_malloc_16(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*)ll_aligned_malloc_16(size);
		static int gl_buffer_idx = 0;
		mGLIndices = ++gl_buffer_idx;

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

void LLVertexBuffer::destroyGLBuffer()
{
			ll_aligned_free_16((void*)mMappedData);
}

void LLVertexBuffer::destroyGLIndices()
{
			ll_aligned_free_16((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;
ruslantproductengine's avatar
ruslantproductengine committed
	static const 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,
ruslantproductengine's avatar
ruslantproductengine committed
	static const 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,
ruslantproductengine's avatar
ruslantproductengine committed
	static const 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
ruslantproductengine's avatar
ruslantproductengine committed
	static const 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))
		{
			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)
				{
					// nat 2018-10-24: VS 2017 also notices the issue
					// described below, and warns even with reinterpret_cast.
					// Cast via intptr_t to make it painfully obvious to the
					// compiler that we're doing this intentionally.
					glVertexAttribIPointer(i, attrib_size[i], attrib_type[i], sTypeSize[i],
										   reinterpret_cast<const GLvoid*>(intptr_t(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.
				glVertexAttribPointer(i, attrib_size[i], attrib_type[i],
										 attrib_normalized[i], sTypeSize[i],
										 reinterpret_cast<GLvoid*>(intptr_t(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);

Graham Linden's avatar
Graham Linden committed
	bool success = true;
Graham Linden's avatar
Graham Linden committed
	success &= updateNumVerts(newnverts);		
	success &= updateNumIndices(newnindices);

		if (mGLArray)
		{ //if size changed, offsets changed
			setupVertexArray();
		}
Graham Linden's avatar
Graham Linden committed
	return success;
	//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, offset, length, 
							GL_MAP_WRITE_BIT | 
							GL_MAP_FLUSH_EXPLICIT_BIT | 
#ifdef GL_ARB_map_buffer_range
							glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
								LL_ERRS() << "Invalid buffer size." << LL_ENDL;
						LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_BUFFER);
						src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER, 0, mSize, 
#endif
					}
				}
				else if (gGLManager.mHasFlushBufferRange)
				{
					if (map_range)
					{
						glBufferParameteriAPPLE(GL_ARRAY_BUFFER, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
						glBufferParameteriAPPLE(GL_ARRAY_BUFFER, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
						src = (U8*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
						src = (U8*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
					src = (U8*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
				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;
					glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
					LL_INFOS() << "GL_ARRAY_BUFFER size is " << size << LL_ENDL;
					glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &buff);
						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;
				glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &elem);
					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, 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, 0, sizeof(U16)*mNumIndices, 
#endif
					}
				}
				else if (gGLManager.mHasFlushBufferRange)
				{
					if (map_range)
					{
						glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE);
						glBufferParameteriAPPLE(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
						src = (U8*) glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
						src = (U8*) glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
					LL_RECORD_BLOCK_TIME(FTM_VBO_MAP_INDEX);
					src = (U8*) glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
				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);
				glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &buff);
					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;
					if (mSize >= length + offset)
					{
						glBufferSubData(GL_ARRAY_BUFFER, offset, length, (U8*)mMappedData + offset);
						glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
						LL_WARNS() << "Attempted to map regions to a buffer that is too small, " 
							<< "mapped size: " << mSize
							<< ", gl buffer size: " << size
							<< ", length: " << length
							<< ", offset: " << offset
							<< LL_ENDL;
					}
				glBufferData(GL_ARRAY_BUFFER, getSize(), nullptr, mUsage); // <alchemy/>
				glBufferSubData(GL_ARRAY_BUFFER, 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, offset, length);
#endif
						}
						else if (gGLManager.mHasFlushBufferRange)
						{
							glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER, offset, length);