Work on limiting memory usage.

This commit is contained in:
David Williams 2014-09-18 16:26:47 +02:00
parent d2bbd6beba
commit b08974c197
5 changed files with 59 additions and 24 deletions

View File

@ -157,8 +157,8 @@ int main(int argc, char *argv[])
openGLWidget.show(); openGLWidget.show();
PerlinNoisePager* pager = new PerlinNoisePager(); PerlinNoisePager* pager = new PerlinNoisePager();
LargeVolume<MaterialDensityPair44> volData(PolyVox::Region::MaxRegion, pager, 256); LargeVolume<MaterialDensityPair44> volData(PolyVox::Region::MaxRegion, pager, 64);
volData.setMemoryUsageLimit(2 * 1024 * 1024); // 2Mb volData.setMemoryUsageLimit(8 * 1024 * 1024); // 8Mb
//createSphereInVolume(volData, 30); //createSphereInVolume(volData, 30);
//createPerlinTerrain(volData); //createPerlinTerrain(volData);

View File

@ -334,6 +334,11 @@ namespace PolyVox
uint8_t m_uBlockSideLengthPower; uint8_t m_uBlockSideLengthPower;
Pager<VoxelType>* m_pPager; Pager<VoxelType>* m_pPager;
// Enough to make sure a blocks and it's neighbours can be loaded, with a few to spare.
static const uint32_t uMinPracticalNoOfBlocks = 32;
// Should preent multi-gigabyte volumes with reasonable block sizes.
static const uint32_t uMaxPracticalNoOfBlocks = 32768;
}; };
} }

View File

@ -51,8 +51,25 @@ namespace PolyVox
if (m_pPager) if (m_pPager)
{ {
// If a pager is available then we can set a sensible limit on our memory usage. // If the user is creating a vast (almost infinite) volume then we can bet they will be
m_uBlockCountLimit = 256; // expecting a high memory usage and will want a fair number of blocks to play around with.
if (regValid == Region::MaxRegion)
{
m_uBlockCountLimit = 1024;
}
else
{
// Otherwise we try to choose a block count to avoid too much thrashing, particularly when iterating
// over the whole volume. This means at least enough blocks to cover one edge of the volume, and ideally
// enough for a whole face. Which face? Longest edge by shortest edge seems like a reasonable guess.
uint32_t longestSide = (std::max)(regValid.getWidthInVoxels(), (std::max)(regValid.getHeightInVoxels(), regValid.getDepthInVoxels()));
uint32_t shortestSide = (std::min)(regValid.getWidthInVoxels(), (std::min)(regValid.getHeightInVoxels(), regValid.getDepthInVoxels()));
longestSide /= m_uBlockSideLength;
shortestSide /= m_uBlockSideLength;
m_uBlockCountLimit = longestSide * shortestSide;
}
} }
else else
{ {
@ -61,6 +78,14 @@ namespace PolyVox
m_uBlockCountLimit = (std::numeric_limits<uint32_t>::max)(); m_uBlockCountLimit = (std::numeric_limits<uint32_t>::max)();
} }
// Make sure the calculated block limit is within practical bounds
m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks);
m_uBlockCountLimit = (std::min)(m_uBlockCountLimit, uMaxPracticalNoOfBlocks);
uint32_t uUncompressedBlockSizeInBytes = UncompressedBlock<VoxelType>::calculateSizeInBytes(m_uBlockSideLength);
POLYVOX_LOG_DEBUG("Memory usage limit for volume initially set to " << (m_uBlockCountLimit * uUncompressedBlockSizeInBytes) / (1024 * 1024)
<< "Mb (" << m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes / 1024 << "Kb each).");
initialise(); initialise();
} }
@ -229,22 +254,24 @@ namespace PolyVox
{ {
POLYVOX_THROW_IF(!m_pPager, invalid_operation, "You cannot limit the memory usage of the volume because it was created without a pager attached."); POLYVOX_THROW_IF(!m_pPager, invalid_operation, "You cannot limit the memory usage of the volume because it was created without a pager attached.");
uint32_t uUncompressedBlockSizeInBytes = m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType); // Calculate the number of blocks based on the memory limit and the size of each block.
uint32_t uUncompressedBlockSizeInBytes = UncompressedBlock<VoxelType>::calculateSizeInBytes(m_uBlockSideLength);
m_uBlockCountLimit = uMemoryUsageInBytes / uUncompressedBlockSizeInBytes; m_uBlockCountLimit = uMemoryUsageInBytes / uUncompressedBlockSizeInBytes;
const uint32_t uMinPracticalNoOfBlocks = 4; // We need at least a few blocks available to avoid thrashing, and in pratice there will probably be hundreds.
POLYVOX_LOG_WARNING_IF(m_uBlockCountLimit < uMinPracticalNoOfBlocks, "The memory usage limit is set too low and cannot be adhered to."); POLYVOX_LOG_WARNING_IF(m_uBlockCountLimit < uMinPracticalNoOfBlocks, "Requested memory usage limit of "
<< uMemoryUsageInBytes / (1024 * 1024) << "Mb is too low and cannot be adhered to.");
m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks); m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks);
m_uBlockCountLimit = (std::min)(m_uBlockCountLimit, uMaxPracticalNoOfBlocks);
// If the new limit is less than the number of blocks already loaded then the easiest solution is to flush and start loading again.
if (m_pRecentlyUsedBlocks.size() > m_uBlockCountLimit) if (m_pRecentlyUsedBlocks.size() > m_uBlockCountLimit)
{ {
flushAll(); flushAll();
} }
POLYVOX_LOG_DEBUG("Target memory usage for volume set to " << uMemoryUsageInBytes << "bytes (" POLYVOX_LOG_DEBUG("Memory usage limit for volume now set to " << (m_uBlockCountLimit * uUncompressedBlockSizeInBytes) / (1024 * 1024)
<< m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes << "bytes each)."); << "Mb (" << m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes / 1024 << "Kb each).");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -579,24 +606,19 @@ namespace PolyVox
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// Note: This function needs reviewing for accuracy... /// Calculate the memory usage of the volume.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType> template <typename VoxelType>
uint32_t LargeVolume<VoxelType>::calculateSizeInBytes(void) uint32_t LargeVolume<VoxelType>::calculateSizeInBytes(void)
{ {
// Purge null blocks so we know that all blocks are used.
purgeNullPtrsFromAllBlocks();
// We include the size of the LargeVolume class but it should be insignificant.
uint32_t uSizeInBytes = sizeof(LargeVolume); uint32_t uSizeInBytes = sizeof(LargeVolume);
// Memory used by the blocks // Memory used by the blocks
typename SharedPtrBlockMap::iterator i; uSizeInBytes += UncompressedBlock<VoxelType>::calculateSizeInBytes(m_uBlockSideLength) * m_pAllBlocks.size();
for (i = m_pRecentlyUsedBlocks.begin(); i != m_pRecentlyUsedBlocks.end(); i++)
{
//Inaccurate - account for rest of loaded block.
uSizeInBytes += i->second->calculateSizeInBytes();
}
//Memory used by the block cache.
//uSizeInBytes += m_vecBlocksWithUncompressedData.capacity() * sizeof(Block<VoxelType>);
//uSizeInBytes += m_vecBlocksWithUncompressedData.size() * m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
return uSizeInBytes; return uSizeInBytes;
} }

View File

@ -64,6 +64,7 @@ namespace PolyVox
// Made this private for consistancy with CompressedBlock. // Made this private for consistancy with CompressedBlock.
// Users shouldn't really need this for UncompressedBlock anyway. // Users shouldn't really need this for UncompressedBlock anyway.
uint32_t calculateSizeInBytes(void); uint32_t calculateSizeInBytes(void);
static uint32_t calculateSizeInBytes(uint32_t uSideLength);
VoxelType* m_tData; VoxelType* m_tData;
uint16_t m_uSideLength; uint16_t m_uSideLength;

View File

@ -140,9 +140,16 @@ namespace PolyVox
template <typename VoxelType> template <typename VoxelType>
uint32_t UncompressedBlock<VoxelType>::calculateSizeInBytes(void) uint32_t UncompressedBlock<VoxelType>::calculateSizeInBytes(void)
{
// Call through to the static version
return calculateSizeInBytes(m_uSideLength);
}
template <typename VoxelType>
uint32_t UncompressedBlock<VoxelType>::calculateSizeInBytes(uint32_t uSideLength)
{ {
// Returns the size of this class plus the size of the uncompressed data. // Returns the size of this class plus the size of the uncompressed data.
uint32_t uSizeInBytes = sizeof(UncompressedBlock<VoxelType>) + (m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType)); uint32_t uSizeInBytes = sizeof(UncompressedBlock<VoxelType>) + (uSideLength * uSideLength * uSideLength * sizeof(VoxelType));
return uSizeInBytes; return uSizeInBytes;
} }
} }