Show buffermanager.cpp syntax highlighted
/***************************************************************************
buffermanager.cpp - Buffer allocation manager
-------------------
begin : Fri Mar 27 2003
copyright : (C) 2003 by Reality Rift Studios
email : mattias@realityrift.com
***************************************************************************
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the NeoEngine, NeoDevOpenGL, buffermanager.cpp
The Initial Developer of the Original Code is Mattias Jansson.
Portions created by Mattias Jansson are Copyright (C) 2003
Reality Rift Studios. All Rights Reserved.
***************************************************************************/
#include "device.h"
#include "buffermanager.h"
#include "bufferregion.h"
#include <neoengine/logstream.h>
#include <assert.h>
using namespace std;
using namespace NeoEngine;
namespace NeoOGL
{
#ifndef NEOGLBUFFERMANAGER_DEBUGALLOC
//Uncomment to get debug messages during allocation/deallocation
//# define NEOGLBUFFERMANAGER_DEBUGALLOC
#endif
#ifndef NEOGLBUFFERMANAGER_FORCECHECK
//Uncomment to force check of buffer integrity after each alloc/dealloc
//# define NEOGLBUFFERMANAGER_FORCECHECK
#endif
bool Device::SetStorageSize( unsigned int uiVertexStorageSize, unsigned int uiPolygonStorageSize )
{
#ifdef WIN32
if( m_kWindow.m_hWnd )
#elif defined( __APPLE__ )
if( m_kWindow.m_pWindow )
#elif defined( POSIX )
if( m_kWindow.m_Window )
#else
# error "Platform not implemented"
return false;
#endif
return false; //Already open
m_uiVertexStorageSize = uiVertexStorageSize;
return true;
}
BufferManager::BufferManager()
{
m_pucBuffer = 0;
m_uiSize = 0;
m_uiOffset = 0;
m_pkTailRegion = 0;
}
BufferManager::~BufferManager()
{
vector< BufferRegion* >::iterator ppkRegion = m_vpkAllocatedRegions.begin();
vector< BufferRegion* >::iterator ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
delete( *ppkRegion );
ppkRegion = m_vpkHoles.begin();
ppkRegionEnd = m_vpkHoles.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
delete( *ppkRegion );
}
BufferRegion *BufferManager::Alloc( unsigned int uiSize,bool bBackingStore, unsigned int uiRequiredAlign )
{
if( !uiSize )
return 0;
//FIXME: With align as implemented below regions can grow multiple times
// Implement new field in region indication post padding size
// and use this extra space before growing region
//FIXME: Use bBackingStore and allow buffers with backing store to be
// swapped out
BufferRegion *pkRegion = 0;
uiSize = ( uiSize + 15 ) & 0xFFFFFFF0;
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Alloc] allocating [" << uiSize << "] bytes" << endl;
#endif
//try to locate hole big enough
vector< BufferRegion* >::iterator ppkHole = m_vpkHoles.begin();
vector< BufferRegion* >::iterator ppkHoleEnd = m_vpkHoles.end();
unsigned int uiCurSize = uiSize;
unsigned int uiPadding = 0;
for( ; ppkHole != ppkHoleEnd; ++ppkHole )
{
if( uiRequiredAlign )
uiCurSize = uiSize + ( uiPadding = (*ppkHole)->m_uiRegionOffset % uiRequiredAlign );
if( (*ppkHole)->m_uiRegionSize >= uiCurSize )
{
if( (*ppkHole)->m_uiRegionSize == uiCurSize )
{
pkRegion = *ppkHole;
//Locate region with border to start of this hole
if( uiPadding && (*ppkHole)->m_uiRegionOffset )
{
vector< BufferRegion* >::iterator ppkRegion = m_vpkAllocatedRegions.begin();
vector< BufferRegion* >::iterator ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
{
if( ( (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize ) == (*ppkHole)->m_uiRegionOffset )
{
//Increase size to align this hole and end of this region
(*ppkRegion)->m_uiRegionSize += uiPadding;
(*ppkHole)->m_uiRegionSize -= uiPadding;
(*ppkHole)->m_uiRegionOffset += uiPadding;
break;
}
}
if( ppkRegion == ppkRegionEnd )
{
//If you hit this assert, buffer integrity failed (non-zero offset hole, no start border region)
assert( false );
neolog << LogLevel( ERROR ) << "[BufferManager::Alloc] unable to locate region at start border" << endl;
}
assert( !( pkRegion->m_uiRegionOffset % uiRequiredAlign ) );
}
m_vpkAllocatedRegions.push_back( pkRegion );
m_vpkHoles.erase( ppkHole );
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Alloc] found exact match in hole vector" << endl;
//DumpAllocationData();
#endif
#ifdef NEOGLBUFFERMANAGER_FORCECHECK
CheckMemoryIntegrity();
#endif
assert( pkRegion );
return pkRegion;
}
pkRegion = new BufferRegion( bBackingStore );
pkRegion->m_uiRegionOffset = (*ppkHole)->m_uiRegionOffset;
pkRegion->m_uiRegionSize = uiCurSize;
(*ppkHole)->m_uiRegionOffset += uiCurSize;
(*ppkHole)->m_uiRegionSize -= uiCurSize;
//Locate region with border to start of this hole
if( uiPadding && pkRegion->m_uiRegionOffset )
{
vector< BufferRegion* >::iterator ppkRegion = m_vpkAllocatedRegions.begin();
vector< BufferRegion* >::iterator ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
{
if( ( (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize ) == pkRegion->m_uiRegionOffset )
{
//Increase size to align this hole and end of this region
(*ppkRegion)->m_uiRegionSize += uiPadding;
pkRegion->m_uiRegionSize -= uiPadding;
pkRegion->m_uiRegionOffset += uiPadding;
break;
}
}
if( ppkRegion == ppkRegionEnd )
{
//If you hit this assert, buffer integrity failed (non-zero offset hole, no start border region)
assert( false );
neolog << LogLevel( ERROR ) << "[BufferManager::Alloc] unable to locate region at start border" << endl;
}
assert( !( pkRegion->m_uiRegionOffset % uiRequiredAlign ) );
}
m_vpkAllocatedRegions.push_back( pkRegion );
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Alloc] used part of hole" << endl;
//DumpAllocationData();
#endif
#ifdef NEOGLBUFFERMANAGER_FORCECHECK
CheckMemoryIntegrity();
#endif
assert( pkRegion );
// ### FIXME: improve performance by using other hole
// or creating new region if this hole
// fence is not finished!
//Make sure fence is finished
if( !(*ppkHole)->TestFence() )
(*ppkHole)->FinishFence();
return pkRegion;
}
}
if( m_uiOffset + uiSize < m_uiSize )
{
pkRegion = new BufferRegion( bBackingStore );
pkRegion->m_uiRegionOffset = m_uiOffset;
pkRegion->m_uiRegionSize = uiSize;
if( uiRequiredAlign && pkRegion->m_uiRegionOffset && ( pkRegion->m_uiRegionOffset % uiRequiredAlign ) )
{
//If you hit this assert, buffer integrity failed (non-zero region offset but no tail region)
assert( m_pkTailRegion );
unsigned int uiPadding = uiRequiredAlign - ( pkRegion->m_uiRegionOffset % uiRequiredAlign );
uiSize += uiPadding;
if( m_uiOffset + uiSize >= m_uiSize )
{
delete pkRegion;
neolog << LogLevel( ERROR ) << "[BufferManager::Alloc] out of memory (region alignment)" << endl;
return 0;
}
m_pkTailRegion->m_uiRegionSize += uiPadding;
pkRegion->m_uiRegionOffset += uiPadding;
assert( !( pkRegion->m_uiRegionOffset % uiRequiredAlign ) );
}
m_pkTailRegion = pkRegion;
m_uiOffset += uiSize;
m_vpkAllocatedRegions.push_back( pkRegion );
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Alloc] new tail region" << endl;
//DumpAllocationData();
#endif
}
else
{
neolog << LogLevel( ERROR ) << "[BufferManager::Alloc] out of memory" << endl;
//DumpAllocationData();
return 0;
}
#ifdef NEOGLBUFFERMANAGER_FORCECHECK
CheckMemoryIntegrity();
#endif
assert( pkRegion );
return pkRegion;
}
void BufferManager::Free( BufferRegion *pkRegion )
{
if( !pkRegion )
return;
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] deallocating region with [" << pkRegion->m_uiRegionSize << "] bytes" << endl;
#endif
//Locate region
vector< BufferRegion* >::iterator ppkRegion = m_vpkAllocatedRegions.begin();
vector< BufferRegion* >::iterator ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
if( (*ppkRegion) == pkRegion )
break;
if( ppkRegion == ppkRegionEnd )
{
//If you hit this assert, you tried freeing a region that was not allocated by this manager, or already deallocated
assert( !"invalid deallocation" );
neolog << LogLevel( ERROR ) << "[BufferManager::Free] invalid region, not in allocated vector" << endl;
return;
}
m_vpkAllocatedRegions.erase( ppkRegion );
bool bIsTail = false;
if( pkRegion == m_pkTailRegion )
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] tail region" << endl;
#endif
if( m_vpkAllocatedRegions.size() )
{
m_pkTailRegion = m_vpkAllocatedRegions[0];
ppkRegion = m_vpkAllocatedRegions.begin() + 1;
ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
if( (*ppkRegion)->m_uiRegionOffset > m_pkTailRegion->m_uiRegionOffset )
m_pkTailRegion = *ppkRegion;
m_uiOffset = m_pkTailRegion->m_uiRegionOffset + m_pkTailRegion->m_uiRegionSize;
}
else
{
m_uiOffset = 0;
m_pkTailRegion = 0;
}
bIsTail = true;
}
//Locate holes that border on pkRegion
int iPreHole = -1;
int iPostHole = -1;
ppkRegion = m_vpkHoles.begin();
ppkRegionEnd = m_vpkHoles.end();
for( unsigned int uiHole = 0; ( ppkRegion != ppkRegionEnd ) && ( ( iPreHole < 0 ) || ( iPostHole < 0 ) ); ++uiHole, ++ppkRegion )
{
if( ( (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize ) == pkRegion->m_uiRegionOffset )
{
//If you hit this assert, buffer integrity has failed (multiple regions with border on freed region
assert( iPreHole < 0 );
iPreHole = (int)uiHole;
}
else if( ( pkRegion->m_uiRegionOffset + pkRegion->m_uiRegionSize ) == (*ppkRegion)->m_uiRegionOffset )
{
//If you hit this assert, buffer integrity has failed (multiple regions with border on freed region
assert( iPostHole < 0 );
iPostHole = (int)uiHole;
}
}
if( ( iPreHole >= 0 ) && ( iPostHole >= 0 ) )
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] both pre and post border holes found" << endl;
#endif
//If you hit this assert, buffer integrity has failed (tail region with border holes on both sides)
assert( !bIsTail );
//Merge into one hole
m_vpkHoles[iPreHole]->m_uiRegionSize += pkRegion->m_uiRegionSize + m_vpkHoles[iPostHole]->m_uiRegionSize;
delete pkRegion;
delete m_vpkHoles[iPostHole];
m_vpkHoles.erase( m_vpkHoles.begin() + iPostHole );
}
else if( iPreHole >= 0 )
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] pre border hole found" << endl;
#endif
m_vpkHoles[iPreHole]->m_uiRegionSize += pkRegion->m_uiRegionSize;
delete pkRegion;
if( bIsTail )
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] tail, erasing pre hole" << endl;
#endif
//If you hit this assert, memory integrity failed (tail region with pre hole offset does not match calculated new start of free memory)
assert( m_uiOffset == m_vpkHoles[iPreHole]->m_uiRegionOffset );
delete m_vpkHoles[iPreHole];
m_vpkHoles.erase( m_vpkHoles.begin() + iPreHole );
}
}
else if( iPostHole >= 0 )
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] post hole found" << endl;
#endif
//If you hit this assert, buffer integrity has failed (tail region with post border hole)
assert( !bIsTail );
m_vpkHoles[iPostHole]->m_uiRegionOffset = pkRegion->m_uiRegionOffset;
m_vpkHoles[iPostHole]->m_uiRegionSize += pkRegion->m_uiRegionSize;
delete pkRegion;
}
else
{
if( !bIsTail )
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] store as new hole" << endl;
#endif
//Insert new hole
m_vpkHoles.push_back( pkRegion );
}
else
{
#ifdef NEOGLBUFFERMANAGER_DEBUGALLOC
neolog << LogLevel( DEBUG ) << "[BufferManager::Free] tail region, deleting" << endl;
#endif
//If you hit this assert, memory integrity failed (tail region without pre hole offset does not match calculated new start of free memory)
assert( m_uiOffset == pkRegion->m_uiRegionOffset );
delete pkRegion;
}
}
//DumpAllocationData();
#ifdef NEOGLBUFFERMANAGER_FORCECHECK
CheckMemoryIntegrity();
#endif
}
void BufferManager::CheckMemoryIntegrity()
{
//Pass 1: Check for overlapping regions
vector< BufferRegion* >::iterator ppkRegion = m_vpkAllocatedRegions.begin();
vector< BufferRegion* >::iterator ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
{
vector< BufferRegion* >::iterator ppkLoopRegion = ppkRegion + 1;
for( ; ppkLoopRegion != ppkRegionEnd; ++ppkLoopRegion )
{
if( ( ( (*ppkRegion)->m_uiRegionOffset >= (*ppkLoopRegion)->m_uiRegionOffset ) && ( (*ppkRegion)->m_uiRegionOffset < ( (*ppkLoopRegion)->m_uiRegionOffset + (*ppkLoopRegion)->m_uiRegionSize ) ) ) ||
( ( (*ppkLoopRegion)->m_uiRegionOffset >= (*ppkRegion)->m_uiRegionOffset ) && ( (*ppkLoopRegion)->m_uiRegionOffset < ( (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize ) ) ) )
{
neolog << LogLevel( ERROR ) << "[BufferManager::CheckMemoryIntegrity] allocated buffer overlap detected" << endl
<< " buffer offset [" << (*ppkRegion)->m_uiRegionOffset << "] size ["
<< (*ppkRegion)->m_uiRegionSize << "] end ["
<< (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize << "]" << endl
<< " buffer offset [" << (*ppkLoopRegion)->m_uiRegionOffset << "] size ["
<< (*ppkLoopRegion)->m_uiRegionSize << "] end [" << (*ppkLoopRegion)->m_uiRegionOffset + (*ppkLoopRegion)->m_uiRegionSize << "]" << endl;
assert( false );
}
}
vector< BufferRegion* >::iterator ppkHole = m_vpkHoles.begin();
vector< BufferRegion* >::iterator ppkHoleEnd = m_vpkHoles.end();
for( ; ppkHole != ppkHoleEnd; ++ppkHole )
{
if( ( ( (*ppkRegion)->m_uiRegionOffset >= (*ppkHole)->m_uiRegionOffset ) && ( (*ppkRegion)->m_uiRegionOffset < ( (*ppkHole)->m_uiRegionOffset + (*ppkHole)->m_uiRegionSize ) ) ) ||
( ( (*ppkHole)->m_uiRegionOffset >= (*ppkRegion)->m_uiRegionOffset ) && ( (*ppkHole)->m_uiRegionOffset < ( (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize ) ) ) )
{
neolog << LogLevel( ERROR ) << "[BufferManager::CheckMemoryIntegrity] allocated buffer/hole overlap detected" << endl
<< " buffer offset [" << (*ppkRegion)->m_uiRegionOffset << "] size [" << (*ppkRegion)->m_uiRegionSize << "] end ["
<< (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize << "]" << endl
<< " hole offset [" << (*ppkHole)->m_uiRegionOffset << "] size [" << (*ppkHole)->m_uiRegionSize
<< "] end [" << (*ppkHole)->m_uiRegionOffset + (*ppkHole)->m_uiRegionSize << "]" << endl;
assert( false );
}
}
}
//Pass 2: Check for overlapping holes
vector< BufferRegion* >::iterator ppkHole = m_vpkHoles.begin();
vector< BufferRegion* >::iterator ppkHoleEnd = m_vpkHoles.end();
for( ; ppkHole != ppkHoleEnd; ++ppkHole )
{
vector< BufferRegion* >::iterator ppkLoopHole = ppkHole + 1;
for( ; ppkLoopHole != ppkHoleEnd; ++ppkLoopHole )
{
if( ( ( (*ppkLoopHole)->m_uiRegionOffset >= (*ppkHole)->m_uiRegionOffset ) && ( (*ppkLoopHole)->m_uiRegionOffset < ( (*ppkHole)->m_uiRegionOffset + (*ppkHole)->m_uiRegionSize ) ) ) ||
( ( (*ppkHole)->m_uiRegionOffset >= (*ppkLoopHole)->m_uiRegionOffset ) && ( (*ppkHole)->m_uiRegionOffset < ( (*ppkLoopHole)->m_uiRegionOffset + (*ppkLoopHole)->m_uiRegionSize ) ) ) )
{
neolog << LogLevel( ERROR ) << "[BufferManager::CheckMemoryIntegrity] hole overlap detected" << endl
<< " hole offset [" << (*ppkLoopHole)->m_uiRegionOffset << "] size [" << (*ppkLoopHole)->m_uiRegionSize
<< "] end [" << (*ppkLoopHole)->m_uiRegionOffset + (*ppkLoopHole)->m_uiRegionSize << "]" << endl
<< " hole offset [" << (*ppkHole)->m_uiRegionOffset << "] size [" << (*ppkHole)->m_uiRegionSize
<< "] end [" << (*ppkHole)->m_uiRegionOffset + (*ppkHole)->m_uiRegionSize << "]" << endl;
assert( false );
}
}
}
//Pass 3: We must either have at least one allocated buffer or no holes
assert( m_pkTailRegion || !m_vpkHoles.size() );
}
void BufferManager::DumpAllocationData()
{
CheckMemoryIntegrity();
neolog << LogLevel( DEBUG ) << "[BufferManager::DumpAllocationData] memory allocations:" << endl;
vector< BufferRegion* >::iterator ppkRegion = m_vpkAllocatedRegions.begin();
vector< BufferRegion* >::iterator ppkRegionEnd = m_vpkAllocatedRegions.end();
for( ; ppkRegion != ppkRegionEnd; ++ppkRegion )
{
neolog << LogLevel( DEBUG ) << " offset [" << (*ppkRegion)->m_uiRegionOffset << "] size [" << (*ppkRegion)->m_uiRegionSize
<< "] end [" << (*ppkRegion)->m_uiRegionOffset + (*ppkRegion)->m_uiRegionSize << "]" << endl;
}
neolog << LogLevel( DEBUG ) << "[BufferManager::DumpAllocationData] memory holes:" << endl;
vector< BufferRegion* >::iterator ppkHole = m_vpkHoles.begin();
vector< BufferRegion* >::iterator ppkHoleEnd = m_vpkHoles.end();
for( ; ppkHole != ppkHoleEnd; ++ppkHole )
{
neolog << LogLevel( DEBUG ) << " offset [" << (*ppkHole)->m_uiRegionOffset << "] size [" << (*ppkHole)->m_uiRegionSize
<< "] end [" << (*ppkHole)->m_uiRegionOffset + (*ppkHole)->m_uiRegionSize << "]" << endl;
}
}
}; // namespace NeoOGL
See more files for this project here