/*
==============
idVertexCache::BeginBackEnd
==============
*/
void idVertexCache::BeginBackEnd() {
	mostUsedVertex = Max( mostUsedVertex, frameData[listNum].vertexMemUsed.GetValue() );
	mostUsedIndex = Max( mostUsedIndex, frameData[listNum].indexMemUsed.GetValue() );
	mostUsedJoint = Max( mostUsedJoint, frameData[listNum].jointMemUsed.GetValue() );

	if ( r_showVertexCache.GetBool() ) {
		idLib::Printf( "%08d: %d allocations, %dkB vertex, %dkB index, %kB joint : %dkB vertex, %dkB index, %kB joint\n", 
			currentFrame, frameData[listNum].allocations,
			frameData[listNum].vertexMemUsed.GetValue() / 1024,
			frameData[listNum].indexMemUsed.GetValue() / 1024,
			frameData[listNum].jointMemUsed.GetValue() / 1024,
			mostUsedVertex / 1024,
			mostUsedIndex / 1024,
			mostUsedJoint / 1024 );
	}

    EndBackEnd();

	drawListNum = listNum;

	// prepare the next frame for writing to by the CPU
	currentFrame++;

	listNum = currentFrame % VERTCACHE_NUM_FRAMES;
	const int startMap = Sys_Milliseconds();
	MapGeoBufferSet( frameData[listNum] );
	const int endMap = Sys_Milliseconds();
	if ( endMap - startMap > 1 ) {
		idLib::PrintfIf( r_showVertexCacheTimings.GetBool(), "idVertexCache::map took %i msec\n", endMap - startMap );
	}

	ClearGeoBufferSet( frameData[listNum] );
}
/*
==============
idVertexCache::BeginBackEnd
==============
*/
void idVertexCache::BeginBackEnd()
{
	mostUsedVertex = Max( mostUsedVertex, frameData[listNum].vertexMemUsed.GetValue() );
	mostUsedIndex = Max( mostUsedIndex, frameData[listNum].indexMemUsed.GetValue() );
	mostUsedJoint = Max( mostUsedJoint, frameData[listNum].jointMemUsed.GetValue() );
	
	if( r_showVertexCache.GetBool() )
	{
		idLib::Printf( "%08d: %d allocations, %dkB vertex, %dkB index, %ikB joint : %dkB vertex, %dkB index, %ikB joint\n",
					   currentFrame, frameData[listNum].allocations,
					   frameData[listNum].vertexMemUsed.GetValue() / 1024,
					   frameData[listNum].indexMemUsed.GetValue() / 1024,
					   frameData[listNum].jointMemUsed.GetValue() / 1024,
					   mostUsedVertex / 1024,
					   mostUsedIndex / 1024,
					   mostUsedJoint / 1024 );
	}
	
	// unmap the current frame so the GPU can read it
	const int startUnmap = Sys_Milliseconds();
	UnmapGeoBufferSet( frameData[listNum] );
	UnmapGeoBufferSet( staticData );
	const int endUnmap = Sys_Milliseconds();
	if( endUnmap - startUnmap > 1 )
	{
		idLib::PrintfIf( r_showVertexCacheTimings.GetBool(), "idVertexCache::unmap took %i msec\n", endUnmap - startUnmap );
	}
	drawListNum = listNum;
	
	// prepare the next frame for writing to by the CPU
	currentFrame++;
	
	listNum = currentFrame % VERTCACHE_NUM_FRAMES;
	const int startMap = Sys_Milliseconds();
	MapGeoBufferSet( frameData[listNum] );
	const int endMap = Sys_Milliseconds();
	if( endMap - startMap > 1 )
	{
		idLib::PrintfIf( r_showVertexCacheTimings.GetBool(), "idVertexCache::map took %i msec\n", endMap - startMap );
	}
	
	ClearGeoBufferSet( frameData[listNum] );
	
	
#if 0
	const int startBind = Sys_Milliseconds();
	glBindBuffer( GL_ARRAY_BUFFER, ( GLuint )frameData[drawListNum].vertexBuffer.GetAPIObject() );
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ( GLuint )frameData[drawListNum].indexBuffer.GetAPIObject() );
	const int endBind = Sys_Milliseconds();
	if( endBind - startBind > 1 )
	{
		idLib::Printf( "idVertexCache::bind took %i msec\n", endBind - startBind );
	}
#endif
	
}
/*
==============
idVertexCache::ActuallyAlloc
==============
*/
vertCacheHandle_t idVertexCache::ActuallyAlloc( geoBufferSet_t & vcs, const void * data, int bytes, cacheType_t type ) {
	if ( bytes == 0 ) {
		return (vertCacheHandle_t)0;
	}

	assert( ( ((UINT_PTR)(data)) & 15 ) == 0 );
	assert( ( bytes & 15 ) == 0 );

	// thread safe interlocked adds
	byte ** base = NULL;
	int	endPos = 0;
	if ( type == CACHE_INDEX ) {
		base = &vcs.mappedIndexBase;
		endPos = vcs.indexMemUsed.Add( bytes );
		if ( endPos > vcs.indexBuffer.GetAllocedSize() ) {
			idLib::Error( "Out of index cache" );
		}
	} else if ( type == CACHE_VERTEX ) {
		base = &vcs.mappedVertexBase;
		endPos = vcs.vertexMemUsed.Add( bytes );
		if ( endPos > vcs.vertexBuffer.GetAllocedSize() ) {
			idLib::Error( "Out of vertex cache" );
		}
	} else if ( type == CACHE_JOINT ) {
		base = &vcs.mappedJointBase;
		endPos = vcs.jointMemUsed.Add( bytes );
		if ( endPos > vcs.jointBuffer.GetAllocedSize() ) {
			idLib::Error( "Out of joint buffer cache" );
		}
	} else {
		assert( false );
	}

	vcs.allocations++;

	int offset = endPos - bytes;

	// Actually perform the data transfer
	if ( data != NULL ) {
		MapGeoBufferSet( vcs );
		CopyBuffer( *base + offset, (const byte *)data, bytes );
	}

	vertCacheHandle_t handle =	( (uint64)(currentFrame & VERTCACHE_FRAME_MASK ) << VERTCACHE_FRAME_SHIFT ) |
								( (uint64)(offset & VERTCACHE_OFFSET_MASK ) << VERTCACHE_OFFSET_SHIFT ) |
								( (uint64)(bytes & VERTCACHE_SIZE_MASK ) << VERTCACHE_SIZE_SHIFT );
	if ( &vcs == &staticData ) {
		handle |= VERTCACHE_STATIC;
	}
	return handle;
}
/*
==============
idVertexCache::Init
==============
*/
void idVertexCache::Init( bool restart ) {
	currentFrame = 0;
	listNum = 0;

	mostUsedVertex = 0;
	mostUsedIndex = 0;
	mostUsedJoint = 0;

	for ( int i = 0; i < VERTCACHE_NUM_FRAMES; i++ ) {
		AllocGeoBufferSet( frameData[i], VERTCACHE_VERTEX_MEMORY_PER_FRAME, VERTCACHE_INDEX_MEMORY_PER_FRAME, VERTCACHE_JOINT_MEMORY_PER_FRAME );
	}
	AllocGeoBufferSet( staticData, STATIC_VERTEX_MEMORY, STATIC_INDEX_MEMORY, 0 );

	MapGeoBufferSet( frameData[listNum] );
}