The idea of a 'setTargetMemoryLimitInBytes' function was not really working out. It was too comp-lex and not really clear what the ideal size would be. I think user code needs to call setMaxNumberOfBlocksInMemory() and setMaxNumberOfUncompressedBlocks() directly, if they see from profiling toat too much camressing or paging is taking place.

This commit is contained in:
David Williams 2013-07-23 13:15:01 +02:00
parent 7cd115b33d
commit 7146b5ecdb
4 changed files with 22 additions and 66 deletions

View File

@ -153,9 +153,8 @@ int main(int argc, char *argv[])
RLECompressor<MaterialDensityPair44, uint16_t>* compressor = new RLECompressor<MaterialDensityPair44, uint16_t>();
PerlinNoisePager* pager = new PerlinNoisePager();
LargeVolume<MaterialDensityPair44> volData(PolyVox::Region::MaxRegion, compressor, pager, 256);
//volData.setMaxNumberOfBlocksInMemory(4096);
//volData.setMaxNumberOfUncompressedBlocks(64);
volData.setTargetMemoryLimitInBytes(128 * 1024 * 1024);
volData.setMaxNumberOfBlocksInMemory(4096);
volData.setMaxNumberOfUncompressedBlocks(64);
//volData.setMaxNumberOfUncompressedBlocks(4096);
//createSphereInVolume(volData, 30);

View File

@ -263,10 +263,10 @@ namespace PolyVox
/// Gets a voxel at the position given by a 3D vector
POLYVOX_DEPRECATED VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const;
void setTargetMemoryLimitInBytes(uint32_t uTargetMemoryLimitInBytes);
/// Sets the number of blocks for which uncompressed data is stored
void setMaxNumberOfUncompressedBlocks(uint32_t uMaxNumberOfUncompressedBlocks);
/// Sets the number of blocks which can be in memory before the paging system starts unloading them
void setMaxNumberOfBlocksInMemory(uint32_t uMaxNumberOfBlocksInMemory);
/// Sets the voxel at the position given by <tt>x,y,z</tt> coordinates
void setVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue, WrapMode eWrapMode = WrapModes::Validate);
/// Sets the voxel at the position given by a 3D vector
@ -346,8 +346,7 @@ namespace PolyVox
mutable Vector3DInt32 m_v3dLastAccessedBlockPos;
mutable UncompressedBlock<VoxelType>* m_pLastAccessedBlock;
uint32_t m_uMaxNumberOfUncompressedBlocks;
uint32_t m_uTargetMemoryLimitInBytes;
uint32_t m_uMaxNumberOfBlocksInMemory;
// The size of the volume
Region m_regValidRegionInBlocks;

View File

@ -243,62 +243,6 @@ namespace PolyVox
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
}
template <typename VoxelType>
void LargeVolume<VoxelType>::setTargetMemoryLimitInBytes(uint32_t uTargetMemoryLimitInBytes)
{
// Set the limit, and then work out how big the uncompressed cache should be.
m_uTargetMemoryLimitInBytes = uTargetMemoryLimitInBytes;
// The size of a single uncompressed block of data.
uint32_t uUncompressedBlockSizeInBytes = m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
// When deciding how to split the memory between compressed and uncompressed blocks it is useful to know the dimensions of the
// volume in blocks. After sorting, the array below will hold the smallest dimension in [0] and the largest dimension in [2].
const uint32_t uNoOfDimensions = 3;
uint32_t uSortedDimensionsInBlocks[uNoOfDimensions];
uSortedDimensionsInBlocks[0] = (getEnclosingRegion().getWidthInVoxels() >> m_uBlockSideLengthPower) + 1;
uSortedDimensionsInBlocks[1] = (getEnclosingRegion().getHeightInVoxels() >> m_uBlockSideLengthPower) + 1;
uSortedDimensionsInBlocks[2] = (getEnclosingRegion().getDepthInVoxels() >> m_uBlockSideLengthPower) + 1;
std::sort(uSortedDimensionsInBlocks, uSortedDimensionsInBlocks + uNoOfDimensions);
const uint32_t uLongestEdgeInBlocks = uSortedDimensionsInBlocks[2];
const uint32_t uLargestFaceSizeInBlocks = uSortedDimensionsInBlocks[1] * uSortedDimensionsInBlocks[2];
// In the ideal situation we will be able to keep an entire face of blocks decompressed in the cache, and still have significant memory
// left for storing a large number of compressed block. Failing this, we would at least like to be able to keep enough blocks decompressed
// to iterate over the longest edge. Otherwise we will find that excessive compression and decompresion occurs. In both cases we multiply
// by an extra factor make sure there is space for storing some compressed data as well.
const uint32_t uFactor = 2; // Just to make sure we leave some space for compressed data as well
const uint32_t uIdealMemoryTarget = uLargestFaceSizeInBlocks * uUncompressedBlockSizeInBytes * uFactor;
const uint32_t uAcceptableMemoryTarget = uLongestEdgeInBlocks * uUncompressedBlockSizeInBytes * uFactor;
if(uTargetMemoryLimitInBytes >= uIdealMemoryTarget)
{
logDebug() << "The target memory limit (" << uTargetMemoryLimitInBytes << "bytes) is plenty for a volume of this size)";
// The added value below is just some extra 'grace' space.
setMaxNumberOfUncompressedBlocks(uLargestFaceSizeInBlocks + uSortedDimensionsInBlocks[1]);
}
else if(uTargetMemoryLimitInBytes >= uAcceptableMemoryTarget)
{
logDebug() << "The target memory limit (" << uTargetMemoryLimitInBytes << "bytes) should be acceptable for a volume of this size)";
// Let's (arbitrarily) use 1/4 of the available memory for the cache, while making sure that it is as long as the longest side.
uint32_t uMaxFittableUncompressedBlocks = uTargetMemoryLimitInBytes / uUncompressedBlockSizeInBytes;
setMaxNumberOfUncompressedBlocks((std::max)(uMaxFittableUncompressedBlocks / 4, uLongestEdgeInBlocks));
}
else
{
logWarning() << "The target memory limit (" << uTargetMemoryLimitInBytes << "bytes) is too low for a volume of this size)";
logWarning() << "Excessive block thrashing may occur and the target memory limit may not be honored";
// We still have to do something. Let's still use 1/4 of the memory for the cache, but also set a minimum size.
const uint32_t uMinNoOfUncompressedBlocks = 8; // Chosen because it just feels about right.
uint32_t uMaxFittableUncompressedBlocks = uTargetMemoryLimitInBytes / uUncompressedBlockSizeInBytes;
setMaxNumberOfUncompressedBlocks((std::max)(uMaxFittableUncompressedBlocks / 4, uMinNoOfUncompressedBlocks));
}
}
////////////////////////////////////////////////////////////////////////////////
/// Increasing the size of the block cache will increase memory but may improve performance.
/// You may want to set this to a large value (e.g. 1024) when you are first loading your
@ -317,6 +261,21 @@ namespace PolyVox
<< ", which is " << m_uMaxNumberOfUncompressedBlocks * uUncompressedBlockSizeInBytes << " bytes";
}
////////////////////////////////////////////////////////////////////////////////
/// Increasing the number of blocks in memory causes less paging to occur
/// \param uMaxNumberOfBlocksInMemory The number of blocks
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void LargeVolume<VoxelType>::setMaxNumberOfBlocksInMemory(uint32_t uMaxNumberOfBlocksInMemory)
{
if(m_pBlocks.size() > uMaxNumberOfBlocksInMemory)
{
flushAll();
}
m_uMaxNumberOfBlocksInMemory = uMaxNumberOfBlocksInMemory;
}
////////////////////////////////////////////////////////////////////////////////
/// \param uXPos the \c x position of the voxel
/// \param uYPos the \c y position of the voxel

View File

@ -281,9 +281,8 @@ TestVolume::TestVolume()
m_pSimpleVolume = new SimpleVolume<int32_t>(region);
m_pLargeVolume = new LargeVolume<int32_t>(region, m_pCompressor, m_pFilePager, 32);
//m_pLargeVolume->setMaxNumberOfBlocksInMemory(32);
//m_pLargeVolume->setMaxNumberOfUncompressedBlocks(16);
m_pLargeVolume->setTargetMemoryLimitInBytes(4 * 1024 * 1024);
m_pLargeVolume->setMaxNumberOfBlocksInMemory(32);
m_pLargeVolume->setMaxNumberOfUncompressedBlocks(16);
//Fill the volume with some data
for(int z = region.getLowerZ(); z <= region.getUpperZ(); z++)