Documentation and tidying.
This commit is contained in:
parent
29e2e14c3a
commit
ad0e923413
@ -382,7 +382,7 @@ void createPerlinVolumeSlow(Volume<MaterialDensityPair44>& volData)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createPerlinVolumeFast(Volume<MaterialDensityPair44>& volData)
|
/*void createPerlinVolumeFast(Volume<MaterialDensityPair44>& volData)
|
||||||
{
|
{
|
||||||
Perlin perlin(2,8,1,234);
|
Perlin perlin(2,8,1,234);
|
||||||
|
|
||||||
@ -428,7 +428,7 @@ void createPerlinVolumeFast(Volume<MaterialDensityPair44>& volData)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
void createPerlinTerrain(Volume<MaterialDensityPair44>& volData)
|
void createPerlinTerrain(Volume<MaterialDensityPair44>& volData)
|
||||||
{
|
{
|
||||||
@ -510,13 +510,14 @@ int main(int argc, char *argv[])
|
|||||||
openGLWidget.show();
|
openGLWidget.show();
|
||||||
|
|
||||||
//Create an empty volume and then place a sphere in it
|
//Create an empty volume and then place a sphere in it
|
||||||
Volume<MaterialDensityPair44> volData(2048, 2048, 256);
|
Volume<MaterialDensityPair44> volData(256, 256, 256);
|
||||||
//createSphereInVolume(volData, 30);
|
//createSphereInVolume(volData, 30);
|
||||||
createPerlinTerrain(volData);
|
createPerlinTerrain(volData);
|
||||||
//createPerlinVolumeSlow(volData);
|
//createPerlinVolumeSlow(volData);
|
||||||
std::cout << "Memory usage: " << volData.sizeInBytes() << std::endl;
|
std::cout << "Memory usage: " << volData.calculateSizeInBytes() << std::endl;
|
||||||
volData.setBlockCacheSize(8);
|
volData.setBlockCacheSize(8);
|
||||||
std::cout << "Memory usage: " << volData.sizeInBytes() << std::endl;
|
std::cout << "Memory usage: " << volData.calculateSizeInBytes() << std::endl;
|
||||||
|
std::cout << "Compression ratio: " << volData.calculateCompressionRatio() << std::endl;
|
||||||
|
|
||||||
/*srand(12345);
|
/*srand(12345);
|
||||||
for(int ct = 0; ct < 1000; ct++)
|
for(int ct = 0; ct < 1000; ct++)
|
||||||
|
@ -58,7 +58,7 @@ namespace PolyVox
|
|||||||
|
|
||||||
void fill(VoxelType tValue);
|
void fill(VoxelType tValue);
|
||||||
void initialise(uint16_t uSideLength);
|
void initialise(uint16_t uSideLength);
|
||||||
uint32_t sizeInBytes(void);
|
uint32_t calculateSizeInBytes(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void compress(void);
|
void compress(void);
|
||||||
|
@ -131,7 +131,7 @@ namespace PolyVox
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename VoxelType>
|
template <typename VoxelType>
|
||||||
uint32_t Block<VoxelType>::sizeInBytes(void)
|
uint32_t Block<VoxelType>::calculateSizeInBytes(void)
|
||||||
{
|
{
|
||||||
uint32_t uSizeInBytes = sizeof(Block<VoxelType>);
|
uint32_t uSizeInBytes = sizeof(Block<VoxelType>);
|
||||||
uSizeInBytes += m_vecCompressedData.capacity() * sizeof(RunlengthEntry<uint16_t>);
|
uSizeInBytes += m_vecCompressedData.capacity() * sizeof(RunlengthEntry<uint16_t>);
|
||||||
|
@ -36,86 +36,81 @@ freely, subject to the following restrictions:
|
|||||||
namespace PolyVox
|
namespace PolyVox
|
||||||
{
|
{
|
||||||
///The Volume class provides a memory efficient method of storing voxel data while also allowing fast access and modification.
|
///The Volume class provides a memory efficient method of storing voxel data while also allowing fast access and modification.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
/// A Volume is essentially a '3D image' in which each element (voxel) is identified
|
/// A Volume is essentially a '3D image' in which each element (voxel) is identified by a three dimensional (x,y,z) coordinate,
|
||||||
/// by a three dimensional (x,y,z) coordinate, rather than the two dimensional (x,y)
|
/// rather than the two dimensional (x,y) coordinate which is used to identify an element (pixel) in a normal image. Within PolyVox,
|
||||||
/// coordinate which is used to identify an element (pixel) in a normal image. Within
|
/// the Volume class is used to store and manipulate our data before we extract our SurfaceMeshs from it.
|
||||||
/// PolyVox, the Volume class is used to store and manipulate our data before we extract
|
|
||||||
/// our SurfaceMeshs from it.
|
|
||||||
///
|
///
|
||||||
/// <b>Data Representaion - feel free to skip</b>
|
/// <b>Data Representaion</b>
|
||||||
/// If stored carelessly, volume data can take up a huge amount of memory. For example, a
|
/// If stored carelessly, volume data can take up a huge amount of memory. For example, a volume of dimensions 1024x1024x1024 with
|
||||||
/// volume of dimensions 1024x1024x1024 with 1 byte per voxel will require 1GB of memory
|
/// 1 byte per voxel will require 1GB of memory if stored in an uncompressed form. Natuarally our Volume class is much more efficient
|
||||||
/// if stored in an uncompressed form. Natuarally our Volume class is much more efficient
|
/// than this and it is worth understanding (at least at a high level) the approach which is used.
|
||||||
/// than this and it is worth understanding (at least at a high level) the approach
|
///
|
||||||
/// which is used.
|
/// Essentially, the Volume class stores its data as a collection of blocks. Each of these block is much smaller than the whole volume,
|
||||||
|
/// for example a typical size might be 32x32x32 voxels (though is is configurable by the user). In this case, a 256x512x1024 volume
|
||||||
|
/// would contain 8x16x32 = 4096 blocks. The data for each block is stored in a compressed form, which uses only a small amout of
|
||||||
|
/// memory but it is hard to modify the data. Therefore, before any given voxel can be modified, its corresponding block must be uncompressed.
|
||||||
|
///
|
||||||
|
/// The compression and decompression of block is a relatively slow process and so we aim to do this as rarely as possible. In order
|
||||||
|
/// to achive this, the volume class store a cache of recently used blocks and their associated uncompressed data. Each time a voxel
|
||||||
|
/// is touched a timestamp is updated on the corresponding block, when the cache becomes full the block with the oldest timestamp is
|
||||||
|
/// recompressed and moved out of the cache.
|
||||||
|
///
|
||||||
|
/// <b>Achieving high compression rates</b>
|
||||||
|
/// Note: This section is theorectical and not well tested. Please let us know if you find the tips below do or do not work.
|
||||||
|
///
|
||||||
|
/// The compression rates which can be achieved can vary significantly depending the nature of the data you are storing, but you can
|
||||||
|
/// encourage high compression rates by making your data as homogenous as possible. If you are simply storing a material with each
|
||||||
|
/// voxel then this will probably happen naturally. Games such as Minecraft which use this approach will typically involve large ares
|
||||||
|
/// of th same material which will compress down well.
|
||||||
|
///
|
||||||
|
/// However, if you are storing density values then you may want to take some care. The advantage of storing smoothly changing values
|
||||||
|
/// is that you can get smooth surfaces extracted, but storing smoothly changing values inside or outside objects (rather than just
|
||||||
|
/// on the boundary) does not benefit the surface and is very hard to compress effectively. You should apply some thresholding to your
|
||||||
|
/// density values to reduce this problem (this threasholding should only be applied to voxels who don't contribute to the surface).
|
||||||
///
|
///
|
||||||
/// Essentially, the Volume class stores its data as a collection of blocks. Each
|
/// For example, suppose you are using layers of 3D Perlin noise to create a 3D terrain (not a heightmap). If you store the raw Perlin
|
||||||
/// of these block is much smaller than the whole volume, for example a typical size
|
/// noise value at each voxel then a slice through the volume might look like the following:
|
||||||
/// might be 32x32x32 voxels (though is is configurable by the user). In this case,
|
///
|
||||||
/// a 256x512x1024 volume would contain 8x16x32 = 4096 blocks. However, it is unlikely that
|
/// <insert image here>
|
||||||
/// all these blocks actually have to be stored because usually there are duplicates
|
///
|
||||||
/// in which case common data can be shared.
|
/// However, by setting high values to be fixed to one and low values to be fixed to zero you can make a slice through your volume look more like this:
|
||||||
///
|
///
|
||||||
/// Identifying duplicate blocks is in general a difficult task which involves looking at pairs
|
/// <insert image here>
|
||||||
/// of blocks and comparing all the voxels. This is a time consuming task which is not amiable
|
///
|
||||||
/// to being performed when the volume is being modified in real time. However, there are two
|
/// The boundary is in the same place and is still smooth, but the large homogenous regions mean the data should compress much more effectively.
|
||||||
/// specific scenarios which are easily spotted and which PolyVox uses to identify block
|
/// Although it may look like you have lost some precision in this process this is only because the images above are constrained to 256
|
||||||
/// sharing opportunities.
|
/// greyscale values, where as true Perlin noise will give you floating point values.
|
||||||
///
|
///
|
||||||
/// -# Homogeneous blocks (those which contain just a single voxel value) are easy to
|
/// <b>Cache-aware traversal</b>
|
||||||
/// spot and fairly common becuase volumes often contain large homogeous regions. Any time
|
/// You might be suprised at just how many cache misses can occur when you traverse the volume in a naive manner. Consider a 1024x1024x1024 volume
|
||||||
/// you change the value of a voxel you have potentially made the block which contains
|
/// with blocks of size 32x32x32. And imagine you iterate over this volume with a simple three-level for loop which iterates over x, the y, then z.
|
||||||
/// it homogeneous. PolyVox does not check the homogeneity immediately as this would slow
|
/// If you start at position (0,0,0) then ny the time you reach position (1023,0,0) you have touched 1024 voxels along one edge of the volume and
|
||||||
/// down the process of modifying voxels, but you can use the tidyUpMemory() function
|
/// have pulled 32 blocks into the cache. By the time you reach (1023,1023,0) you have hit 1024x1024 voxels and pulled 32x32 blocks into the cache.
|
||||||
/// to check for and remove duplicate homogeneous regions whenever you have spare
|
/// You are now ready to touch voxel (0,0,1) which is right nect to where you started, but unless your cache is at least 32x32 blocks large then this
|
||||||
/// processing time.
|
/// initial block has already been cleared from the cache.
|
||||||
///
|
///
|
||||||
/// -# Copying a volume naturally means that all the voxels in the second volume are
|
/// Ensuring you have a large enough cache size can obviously help the above situation, but you might also consider iterating over the voxels in a
|
||||||
/// the same as the first. Therefore volume copying is a relatively fast operation in
|
/// different order. For example, if you replace your three-level loop with a six-level loop then you can first process all the voxels between (0,0,0)
|
||||||
/// which all the blocks in the second volume simply reference the first volume. Future
|
/// and (31,31,31), then process all the voxels between (32,0,0) and (63,0,0), and so forth. Using this approach you will have no cache misses even
|
||||||
/// modifications to either volume will, of course, cause the blocks to become unshared.
|
/// is your cache sise is only one. Of course the logic is more complex, but writing code in such a cache-aware manner may be beneficial in some situations.
|
||||||
///
|
///
|
||||||
/// Other advantages of breaking the volume down into blocks include enhancing data locality
|
/// <b>Threading</b>
|
||||||
/// (i.e. voxels which are spatially near to each other are also likely to be near in
|
/// The volume class does not provide any thread safety constructs and can therefore not be assumed to be thread safe. To be safe you should only allow
|
||||||
/// memory) and the ability to load larger volumes as no large contiguous areas of
|
/// one thread to access the volume at a time. Even if you have several threads just reading data from the volume they can cause blocks to be pushed
|
||||||
/// memory are needed. However, these advantages are more transparent to user code
|
/// out of the cache, potentially invalidating any pointers other threads might be using.
|
||||||
/// so we will not dwell on them here.
|
///
|
||||||
///
|
/// That said, we believe that if care is taken then multiple threads can be used, and are currently experimenting with this.
|
||||||
/// <b>Usage</b>
|
///
|
||||||
/// Volumes are constructed by passing the desired width, height and depth to the
|
/// <b>Use of templates</b>
|
||||||
/// constructor. Note that for speed reasons only values which are a power of two
|
/// Although this class is templatised on the voxel type it is not expected that you can use any primative type to represent your voxels. It is only
|
||||||
/// are permitted for these sidelengths.
|
/// intended for PolyVox's voxel types such as Material, Density, and MarterialDensityPair. If you need to store 3D grids of ints, floats, or pointers
|
||||||
///
|
/// you should look ar the Array class instead.
|
||||||
/// \code
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
/// Volume<uint8_t> volData(g_uVolumeSideLength, g_uVolumeSideLength, g_uVolumeSideLength);
|
|
||||||
/// \endcode
|
|
||||||
///
|
|
||||||
/// Access to specific voxels is provided by the getVoxelAt() and setVoxelAt() fuctions.
|
|
||||||
/// Each of these has two forms so that voxels can be identified by integer triples
|
|
||||||
/// or by Vector3DUint16%s.
|
|
||||||
///
|
|
||||||
/// \code
|
|
||||||
/// volData.setVoxelAt(12, 186, 281, 3);
|
|
||||||
/// uint8_t voxelValue = volData.getVoxelAt(12, 186, 281);
|
|
||||||
/// //voxelValue is now 3
|
|
||||||
/// \endcode
|
|
||||||
///
|
|
||||||
/// The tidyUpMemory() function should normally be called after you first populate
|
|
||||||
/// the volume with data, and then at periodic intervals as the volume is modified.
|
|
||||||
/// However, you don't actually <i>have</i> to call it at all. See the function's
|
|
||||||
/// documentation for further details.
|
|
||||||
///
|
|
||||||
/// One further important point of note is that this class is templatised on the voxel
|
|
||||||
/// type. This allows you to store volumes of data types you might not normally expect,
|
|
||||||
/// for example the OpenGL example 'abuses' this class to store a 3D grid of pointers.
|
|
||||||
/// However, it is not guaranteed that all functionality works correctly with non-integer
|
|
||||||
/// voxel types.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template <typename VoxelType>
|
template <typename VoxelType>
|
||||||
class Volume
|
class Volume
|
||||||
{
|
{
|
||||||
//Make VolumeSampler a friend
|
// Make VolumeSampler a friend
|
||||||
friend class VolumeSampler<VoxelType>;
|
friend class VolumeSampler<VoxelType>;
|
||||||
|
|
||||||
struct UncompressedBlock
|
struct UncompressedBlock
|
||||||
@ -126,49 +121,49 @@ namespace PolyVox
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
///Constructor
|
/// Constructor
|
||||||
Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32);
|
Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32);
|
||||||
///Destructor
|
/// Destructor
|
||||||
~Volume();
|
~Volume();
|
||||||
|
|
||||||
///Gets the value used for voxels which are outside the volume
|
/// Gets the value used for voxels which are outside the volume
|
||||||
VoxelType getBorderValue(void) const;
|
VoxelType getBorderValue(void) const;
|
||||||
///Gets a Region representing the extents of the Volume.
|
/// Gets a Region representing the extents of the Volume.
|
||||||
Region getEnclosingRegion(void) const;
|
Region getEnclosingRegion(void) const;
|
||||||
///Gets the width of the volume in voxels.
|
/// Gets the width of the volume in voxels.
|
||||||
uint16_t getWidth(void) const;
|
uint16_t getWidth(void) const;
|
||||||
///Gets the height of the volume in voxels.
|
/// Gets the height of the volume in voxels.
|
||||||
uint16_t getHeight(void) const;
|
uint16_t getHeight(void) const;
|
||||||
///Gets the depth of the volume in voxels.
|
/// Gets the depth of the volume in voxels.
|
||||||
uint16_t getDepth(void) const;
|
uint16_t getDepth(void) const;
|
||||||
///Gets the length of the longest side in voxels
|
/// Gets the length of the longest side in voxels
|
||||||
uint16_t getLongestSideLength(void) const;
|
uint16_t getLongestSideLength(void) const;
|
||||||
///Gets the length of the shortest side in voxels
|
/// Gets the length of the shortest side in voxels
|
||||||
uint16_t getShortestSideLength(void) const;
|
uint16_t getShortestSideLength(void) const;
|
||||||
///Gets the length of the diagonal in voxels
|
/// Gets the length of the diagonal in voxels
|
||||||
float getDiagonalLength(void) const;
|
float getDiagonalLength(void) const;
|
||||||
///Gets a voxel by <tt>x,y,z</tt> position
|
/// Gets a voxel by <tt>x,y,z</tt> position
|
||||||
VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
|
VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
|
||||||
///Gets a voxel by 3D vector position
|
/// Gets a voxel by 3D vector position
|
||||||
VoxelType getVoxelAt(const Vector3DUint16& v3dPos) const;
|
VoxelType getVoxelAt(const Vector3DUint16& v3dPos) const;
|
||||||
|
|
||||||
///Sets the value used for voxels which are outside the volume
|
/// Sets the number of blocks for which uncompressed data is stored.
|
||||||
|
void setBlockCacheSize(uint16_t uBlockCacheSize);
|
||||||
|
/// Sets the value used for voxels which are outside the volume
|
||||||
void setBorderValue(const VoxelType& tBorder);
|
void setBorderValue(const VoxelType& tBorder);
|
||||||
///Sets the voxel at an <tt>x,y,z</tt> position
|
/// Sets the voxel at an <tt>x,y,z</tt> position
|
||||||
bool setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue);
|
bool setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue);
|
||||||
///Sets the voxel at a 3D vector position
|
/// Sets the voxel at a 3D vector position
|
||||||
bool setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue);
|
bool setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue);
|
||||||
|
|
||||||
///Resizes the volume to the specified dimensions
|
|
||||||
void resize(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32);
|
|
||||||
|
|
||||||
void setBlockCacheSize(uint16_t uBlockCacheSize);
|
|
||||||
void clearBlockCache(void);
|
void clearBlockCache(void);
|
||||||
|
float calculateCompressionRatio(void);
|
||||||
|
uint32_t calculateSizeInBytes(void);
|
||||||
|
|
||||||
uint32_t sizeInBytes(void);
|
private:
|
||||||
|
|
||||||
public:
|
|
||||||
Block<VoxelType>* getUncompressedBlock(uint16_t uBlockX, uint16_t uBlockY, uint16_t uBlockZ) const;
|
Block<VoxelType>* getUncompressedBlock(uint16_t uBlockX, uint16_t uBlockY, uint16_t uBlockZ) const;
|
||||||
|
/// Resizes the volume to the specified dimensions
|
||||||
|
void resize(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32);
|
||||||
|
|
||||||
//The block data
|
//The block data
|
||||||
mutable std::vector< Block<VoxelType> > m_pBlocks;
|
mutable std::vector< Block<VoxelType> > m_pBlocks;
|
||||||
|
@ -198,6 +198,20 @@ namespace PolyVox
|
|||||||
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
|
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// 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
|
||||||
|
/// volume data and then set it to a smaller value (e.g.64) for general processing.
|
||||||
|
/// \param uBlockCacheSize The number of blocks for which uncompressed data can be cached.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename VoxelType>
|
||||||
|
void Volume<VoxelType>::setBlockCacheSize(uint16_t uBlockCacheSize)
|
||||||
|
{
|
||||||
|
clearBlockCache();
|
||||||
|
|
||||||
|
m_uMaxUncompressedBlockCacheSize = uBlockCacheSize;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// \param tBorder The value to use for voxels outside the volume.
|
/// \param tBorder The value to use for voxels outside the volume.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -352,14 +366,6 @@ namespace PolyVox
|
|||||||
m_fDiagonalLength = sqrtf(static_cast<float>(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth));
|
m_fDiagonalLength = sqrtf(static_cast<float>(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename VoxelType>
|
|
||||||
void Volume<VoxelType>::setBlockCacheSize(uint16_t uBlockCacheSize)
|
|
||||||
{
|
|
||||||
clearBlockCache();
|
|
||||||
|
|
||||||
m_uMaxUncompressedBlockCacheSize = uBlockCacheSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename VoxelType>
|
template <typename VoxelType>
|
||||||
Block<VoxelType>* Volume<VoxelType>::getUncompressedBlock(uint16_t uBlockX, uint16_t uBlockY, uint16_t uBlockZ) const
|
Block<VoxelType>* Volume<VoxelType>::getUncompressedBlock(uint16_t uBlockX, uint16_t uBlockY, uint16_t uBlockZ) const
|
||||||
{
|
{
|
||||||
@ -424,14 +430,22 @@ namespace PolyVox
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename VoxelType>
|
template <typename VoxelType>
|
||||||
uint32_t Volume<VoxelType>::sizeInBytes(void)
|
float Volume<VoxelType>::calculateCompressionRatio(void)
|
||||||
|
{
|
||||||
|
float fRawSize = m_uWidth * m_uHeight * m_uDepth * sizeof(VoxelType);
|
||||||
|
float fCompressedSize = calculateSizeInBytes();
|
||||||
|
return fCompressedSize/fRawSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename VoxelType>
|
||||||
|
uint32_t Volume<VoxelType>::calculateSizeInBytes(void)
|
||||||
{
|
{
|
||||||
uint32_t uSizeInBytes = sizeof(Volume);
|
uint32_t uSizeInBytes = sizeof(Volume);
|
||||||
|
|
||||||
//Memory used by the blocks
|
//Memory used by the blocks
|
||||||
for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i)
|
for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i)
|
||||||
{
|
{
|
||||||
uSizeInBytes += m_pBlocks[i].sizeInBytes();
|
uSizeInBytes += m_pBlocks[i].calculateSizeInBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Memory used by the block cache.
|
//Memory used by the block cache.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user