Refactoring and tidying.
This commit is contained in:
		| @@ -33,11 +33,21 @@ namespace PolyVox | ||||
| 	template <typename VoxelType> | ||||
| 	class Block | ||||
| 	{ | ||||
| 		template <typename LengthType> | ||||
| 		struct RunlengthEntry | ||||
| 		{ | ||||
| 			LengthType length; | ||||
| 			VoxelType value; | ||||
|  | ||||
| 			//We can parametise the length on anything up to uint32_t. | ||||
| 			//This lets us experiment with the optimal size in the future. | ||||
| 			static uint32_t maxRunlength(void) {return (std::numeric_limits<LengthType>::max)();} | ||||
| 		}; | ||||
|  | ||||
| 		//Make VolumeSampler a friend | ||||
| 		friend class VolumeSampler<VoxelType>; | ||||
| 	public: | ||||
| 		Block(uint16_t uSideLength = 0); | ||||
| 		~Block(); | ||||
|  | ||||
| 		uint16_t getSideLength(void) const; | ||||
| 		VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const; | ||||
| @@ -47,22 +57,20 @@ namespace PolyVox | ||||
| 		void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue); | ||||
|  | ||||
| 		void fill(VoxelType tValue); | ||||
| 		void resize(uint16_t uSideLength); | ||||
| 		uint32_t sizeInChars(void); | ||||
| 		void initialise(uint16_t uSideLength); | ||||
| 		uint32_t sizeInBytes(void); | ||||
|  | ||||
| 	public: | ||||
| 		void compress(void); | ||||
| 		void uncompress(void); | ||||
| 		void uncompress(VoxelType* pData); | ||||
|  | ||||
| 		std::vector< RunlengthEntry<uint16_t> > m_vecCompressedData; | ||||
| 		uint64_t m_uTimestamp; | ||||
| 		VoxelType* m_tUncompressedData; | ||||
| 		uint16_t m_uSideLength; | ||||
| 		uint8_t m_uSideLengthPower;	 | ||||
| 		VoxelType* m_tUncompressedData; | ||||
| 		bool m_bIsCompressed; | ||||
| 		bool m_bIsUncompressedDataModified; | ||||
| 		uint64_t m_uTimestamp; | ||||
|  | ||||
| 		std::vector<uint16_t> runlengths; | ||||
| 		std::vector<VoxelType> values; | ||||
| 		bool m_bIsUncompressedDataModified;		 | ||||
|  | ||||
| 	private: | ||||
| 		Block(const Block& rhs); | ||||
|   | ||||
| @@ -43,7 +43,7 @@ namespace PolyVox | ||||
| 	{ | ||||
| 		if(uSideLength != 0) | ||||
| 		{ | ||||
| 			resize(uSideLength); | ||||
| 			initialise(uSideLength); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -53,13 +53,6 @@ namespace PolyVox | ||||
| 		assert(false); | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	Block<VoxelType>::~Block() | ||||
| 	{ | ||||
| 		delete[] m_tUncompressedData; | ||||
| 		m_tUncompressedData = 0; | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	Block<VoxelType>& Block<VoxelType>::operator=(const Block<VoxelType>& rhs) | ||||
| 	{ | ||||
| @@ -124,11 +117,10 @@ namespace PolyVox | ||||
| 	template <typename VoxelType> | ||||
| 	void Block<VoxelType>::fill(VoxelType tValue) | ||||
| 	{ | ||||
| 		//The memset *may* be faster than the std::fill(), but it doesn't compile nicely | ||||
| 		//in 64-bit mode as casting the pointer to an int causes a loss of precision. | ||||
|  | ||||
| 		assert(m_tUncompressedData); | ||||
|  | ||||
| 		//The memset *may* be faster than the std::fill(), but it doesn't compile nicely | ||||
| 		//in 64-bit mode as casting the pointer to an int causes a loss of precision. | ||||
| 		const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; | ||||
| 		std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue); | ||||
|  | ||||
| @@ -136,7 +128,7 @@ namespace PolyVox | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	void Block<VoxelType>::resize(uint16_t uSideLength) | ||||
| 	void Block<VoxelType>::initialise(uint16_t uSideLength) | ||||
| 	{ | ||||
| 		//Debug mode validation | ||||
| 		assert(isPowerOf2(uSideLength)); | ||||
| @@ -150,120 +142,77 @@ namespace PolyVox | ||||
| 		//Compute the side length		 | ||||
| 		m_uSideLength = uSideLength; | ||||
| 		m_uSideLengthPower = logBase2(uSideLength); | ||||
|  | ||||
|  | ||||
| 		if(m_bIsCompressed == false) | ||||
| 		{ | ||||
| 			//Delete the old data | ||||
| 			delete[] m_tUncompressedData; | ||||
| 			m_tUncompressedData = 0; | ||||
|  | ||||
| 			//If this fails an exception will be thrown. Memory is not    | ||||
| 			//allocated and there is nothing else in this class to clean up | ||||
| 			m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; | ||||
|  | ||||
| 			m_bIsUncompressedDataModified = true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	uint32_t Block<VoxelType>::sizeInChars(void) | ||||
| 	uint32_t Block<VoxelType>::sizeInBytes(void) | ||||
| 	{ | ||||
| 		uint32_t uSizeInChars = 0; //sizeof(Block<VoxelType>); | ||||
|  | ||||
| 		if(m_tUncompressedData != 0) | ||||
| 		{ | ||||
| 			const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; | ||||
| 			uSizeInChars += uNoOfVoxels * sizeof(VoxelType); | ||||
| 		} | ||||
|  | ||||
| 		uSizeInChars += values.size() * sizeof(VoxelType); | ||||
| 		uSizeInChars += runlengths.size() * sizeof(uint16_t); | ||||
|  | ||||
| 		return  uSizeInChars; | ||||
| 		uint32_t uSizeInBytes = sizeof(Block<VoxelType>); | ||||
| 		uSizeInBytes += m_vecCompressedData.size() * sizeof(RunlengthEntry); | ||||
| 		return  uSizeInBytes; | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	void Block<VoxelType>::compress(void) | ||||
| 	{ | ||||
| 		assert(m_bIsCompressed == false); | ||||
| 		assert(m_tUncompressedData != 0); | ||||
|  | ||||
| 		//If the uncompressed data hasn't actually been | ||||
| 		//modified then we don't need to redo the compression. | ||||
| 		if(m_bIsUncompressedDataModified) | ||||
| 		{ | ||||
| 			uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; | ||||
| 			runlengths.clear(); | ||||
| 			values.clear(); | ||||
| 			m_vecCompressedData.clear(); | ||||
|  | ||||
| 			VoxelType current = m_tUncompressedData[0]; | ||||
| 			uint16_t runLength = 1; | ||||
| 			RunlengthEntry<uint16_t> entry; | ||||
| 			entry.length = 1; | ||||
| 			entry.value = m_tUncompressedData[0]; | ||||
|  | ||||
| 			for(uint32_t ct = 1; ct < uNoOfVoxels; ++ct) | ||||
| 			{		 | ||||
| 				VoxelType value = m_tUncompressedData[ct]; | ||||
| 				if((value == current) && (runLength < (std::numeric_limits<uint16_t>::max)())) | ||||
| 				if((value == entry.value) && (entry.length < entry.maxRunlength())) | ||||
| 				{ | ||||
| 					runLength++; | ||||
| 					entry.length++; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					runlengths.push_back(runLength); | ||||
| 					values.push_back(current); | ||||
| 					current = value; | ||||
| 					runLength = 1; | ||||
| 					m_vecCompressedData.push_back(entry); | ||||
| 					entry.value = value; | ||||
| 					entry.length = 1; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			runlengths.push_back(runLength); | ||||
| 			values.push_back(current); | ||||
| 			m_vecCompressedData.push_back(entry); | ||||
|  | ||||
| 			//Shrink the vectors to their contents (seems slow?): | ||||
| 			//http://stackoverflow.com/questions/1111078/reduce-the-capacity-of-an-stl-vector | ||||
| 			//C++0x may have a shrink_to_fit() function? | ||||
| 			//std::vector<uint8_t>(runlengths).swap(runlengths); | ||||
| 			//std::vector<VoxelType>(values).swap(values); | ||||
| 			//std::vector<RunlengthEntry>(m_vecCompressedData).swap(m_vecCompressedData); | ||||
| 		} | ||||
|  | ||||
| 		assert(m_tUncompressedData != 0); | ||||
| 		delete[] m_tUncompressedData; | ||||
| 		//Flag the uncompressed data as no longer being used but don't delete it (we don't own it). | ||||
| 		m_tUncompressedData = 0; | ||||
| 		m_bIsCompressed = true; | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	void Block<VoxelType>::uncompress(void) | ||||
| 	void Block<VoxelType>::uncompress(VoxelType* pData) | ||||
| 	{ | ||||
| 		assert(m_bIsCompressed == true); | ||||
| 		assert(m_tUncompressedData == 0); | ||||
| 		m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; | ||||
| 		m_tUncompressedData = pData; | ||||
|  | ||||
| 		VoxelType* pUncompressedData = m_tUncompressedData; | ||||
| 		 | ||||
| 		//memset should provide the fastest way of expanding the data, but it works | ||||
| 		//on unsigned chars so is only possible if our voxel type is the right size. | ||||
| 		//Nore that memset takes an int type, but sonverts it to unsiogned char: | ||||
| 		//http://www.cplusplus.com/reference/clibrary/cstring/memset/ | ||||
| 		/*if(sizeof(VoxelType) == sizeof(unsigned char)) | ||||
| 		VoxelType* pUncompressedData = m_tUncompressedData;		 | ||||
| 		for(uint32_t ct = 0; ct < m_vecCompressedData.size(); ++ct) | ||||
| 		{ | ||||
| 			for(uint32_t ct = 0; ct < runlengths.size(); ++ct) | ||||
| 			for(uint32_t i = 0; i < m_vecCompressedData[ct].length; ++i) | ||||
| 			{ | ||||
| 				memset(pUncompressedData, *((int*)(&values[ct])), runlengths[ct]); | ||||
| 				pUncompressedData += runlengths[ct]; | ||||
| 				*pUncompressedData = m_vecCompressedData[ct].value; | ||||
| 				++pUncompressedData; | ||||
| 			} | ||||
| 		} | ||||
| 		//Otherwise we fall back on a loop. | ||||
| 		else | ||||
| 		{*/ | ||||
| 			for(uint32_t ct = 0; ct < runlengths.size(); ++ct) | ||||
| 			{ | ||||
| 				for(uint32_t i = 0; i < runlengths[ct]; ++i) | ||||
| 				{ | ||||
| 					*pUncompressedData = values[ct]; | ||||
| 					++pUncompressedData; | ||||
| 				} | ||||
| 			} | ||||
| 		//} | ||||
|  | ||||
| 		m_bIsCompressed = false; | ||||
| 		m_bIsUncompressedDataModified = false; | ||||
|   | ||||
| @@ -118,6 +118,13 @@ namespace PolyVox | ||||
| 		//Make VolumeSampler a friend | ||||
| 		friend class VolumeSampler<VoxelType>; | ||||
|  | ||||
| 		struct UncompressedBlock | ||||
| 		{ | ||||
| 			Block<VoxelType>* block; | ||||
| 			VoxelType* data; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 	public:		 | ||||
| 		///Constructor | ||||
| 		Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32); | ||||
| @@ -158,15 +165,18 @@ namespace PolyVox | ||||
| 		void setBlockCacheSize(uint16_t uBlockCacheSize); | ||||
| 		void clearBlockCache(void); | ||||
|  | ||||
| 		uint32_t sizeInChars(void); | ||||
| 		uint32_t sizeInBytes(void); | ||||
|  | ||||
| 	public: | ||||
| 		Block<VoxelType>* getUncompressedBlock(Block<VoxelType>* block) const; | ||||
|  | ||||
| 		Block<VoxelType> m_pBorderBlock; | ||||
| 		Block<VoxelType>* m_pBlocks; | ||||
| 		mutable std::vector<Block<VoxelType>*> m_pUncompressedBlocks; | ||||
| 		uint16_t m_uBlockCacheSize; | ||||
| 		//mutable std::vector<Block<VoxelType>*> m_pUncompressedBlocks; | ||||
| 		//mutable std::vector< std::vector<VoxelType> > m_pUncompressedBlockData; | ||||
| 		//mutable VoxelType* m_pUncompressedBlockData; | ||||
| 		mutable std::vector< UncompressedBlock > m_vecUncompressedBlockCache; | ||||
| 		uint16_t m_uMaxUncompressedBlockCacheSize; | ||||
|  | ||||
| 		uint32_t m_uNoOfBlocksInVolume; | ||||
|  | ||||
|   | ||||
| @@ -49,13 +49,13 @@ namespace PolyVox | ||||
| 	template <typename VoxelType> | ||||
| 	Volume<VoxelType>::Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength) | ||||
| 		:m_uTimestamper(0) | ||||
| 		,m_uBlockCacheSize(256) | ||||
| 		,m_uMaxUncompressedBlockCacheSize(256) | ||||
| 		,m_uCompressions(0) | ||||
| 		,m_uUncompressions(0) | ||||
| 		,m_uBlockSideLength(uBlockSideLength) | ||||
| 		,m_pBlocks(0) | ||||
| 	{ | ||||
| 		setBlockCacheSize(m_uBlockCacheSize); | ||||
| 		setBlockCacheSize(m_uMaxUncompressedBlockCacheSize); | ||||
|  | ||||
| 		//Create a volume of the right size. | ||||
| 		resize(uWidth, uHeight, uDepth, uBlockSideLength); | ||||
| @@ -272,13 +272,13 @@ namespace PolyVox | ||||
| 	template <typename VoxelType> | ||||
| 	void Volume<VoxelType>::clearBlockCache(void) | ||||
| 	{ | ||||
| 		for(uint32_t ct = 0; ct < m_pUncompressedBlocks.size(); ct++) | ||||
| 		for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++) | ||||
| 		{ | ||||
| 			m_pUncompressedBlocks[ct]->compress(); | ||||
| 			m_vecUncompressedBlockCache[ct].block->compress(); | ||||
| 			delete[] m_vecUncompressedBlockCache[ct].data; | ||||
| 			m_uCompressions++; | ||||
| 		} | ||||
|  | ||||
| 		m_pUncompressedBlocks.clear(); | ||||
| 		m_vecUncompressedBlockCache.clear(); | ||||
| 	} | ||||
|  | ||||
| 	//////////////////////////////////////////////////////////////////////////////// | ||||
| @@ -350,11 +350,11 @@ namespace PolyVox | ||||
| 		m_pBlocks = new Block<VoxelType>[m_uNoOfBlocksInVolume]; | ||||
| 		for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) | ||||
| 		{ | ||||
| 			m_pBlocks[i].resize(m_uBlockSideLength); | ||||
| 			m_pBlocks[i].initialise(m_uBlockSideLength); | ||||
| 		} | ||||
|  | ||||
| 		//Create the border block | ||||
| 		m_pBorderBlock.resize(uBlockSideLength); | ||||
| 		m_pBorderBlock.initialise(uBlockSideLength); | ||||
| 		Block<VoxelType>* pUncompressedBorderBlock = getUncompressedBlock(&m_pBorderBlock); | ||||
| 		pUncompressedBorderBlock->fill(VoxelType()); | ||||
|  | ||||
| @@ -369,7 +369,19 @@ namespace PolyVox | ||||
| 	{ | ||||
| 		clearBlockCache(); | ||||
|  | ||||
| 		m_uBlockCacheSize = uBlockCacheSize; | ||||
| 		m_uMaxUncompressedBlockCacheSize = uBlockCacheSize; | ||||
|  | ||||
| 		/*m_pUncompressedBlockCache.resize(uBlockCacheSize); | ||||
| 		for(uint32_t ct = 0; ct < m_pUncompressedBlockData.size(); ct++) | ||||
| 		{ | ||||
| 			m_pUncompressedBlockData[ct].data | ||||
| 			VoxelType empty; | ||||
| 			empty.setMaterial(0); | ||||
| 			empty.setDensity(0); | ||||
| 			std::fill(m_pUncompressedBlockData[ct].begin(), m_pUncompressedBlockData[ct].end(), empty); | ||||
| 		} | ||||
| 		m_pUncompressedBlockData = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * m_uBlockCacheSize]; | ||||
| 		memset(m_pUncompressedBlockData, 0, m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * m_uBlockCacheSize);*/ | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| @@ -384,43 +396,52 @@ namespace PolyVox | ||||
|  | ||||
| 		uint32_t uUncompressedBlockIndex = 100000000; | ||||
|  | ||||
| 		assert(m_pUncompressedBlocks.size() <= m_uBlockCacheSize); | ||||
| 		if(m_pUncompressedBlocks.size() == m_uBlockCacheSize) | ||||
| 		assert(m_vecUncompressedBlockCache.size() <= m_uMaxUncompressedBlockCacheSize); | ||||
| 		if(m_vecUncompressedBlockCache.size() == m_uMaxUncompressedBlockCacheSize) | ||||
| 		{ | ||||
| 			int32_t leastRecentlyUsedBlockIndex = -1; | ||||
| 			uint64_t uLeastRecentTimestamp = 1000000000000000; | ||||
| 			for(uint32_t ct = 0; ct < m_pUncompressedBlocks.size(); ct++) | ||||
| 			for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++) | ||||
| 			{ | ||||
| 				if(m_pUncompressedBlocks[ct]->m_uTimestamp < uLeastRecentTimestamp) | ||||
| 				if(m_vecUncompressedBlockCache[ct].block->m_uTimestamp < uLeastRecentTimestamp) | ||||
| 				{ | ||||
| 					uLeastRecentTimestamp = m_pUncompressedBlocks[ct]->m_uTimestamp; | ||||
| 					uLeastRecentTimestamp = m_vecUncompressedBlockCache[ct].block->m_uTimestamp; | ||||
| 					leastRecentlyUsedBlockIndex = ct; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			m_pUncompressedBlocks[leastRecentlyUsedBlockIndex]->compress(); | ||||
| 			uUncompressedBlockIndex = leastRecentlyUsedBlockIndex; | ||||
| 			m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].block->compress(); | ||||
| 			m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].block->m_tUncompressedData = 0; | ||||
| 			m_uCompressions++; | ||||
| 			m_pUncompressedBlocks[leastRecentlyUsedBlockIndex] = block; | ||||
| 			m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].block = block; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			m_pUncompressedBlocks.push_back(block); | ||||
| 			UncompressedBlock uncompressedBlock; | ||||
| 			uncompressedBlock.block = block; | ||||
| 			uncompressedBlock.data = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength]; | ||||
| 			m_vecUncompressedBlockCache.push_back(uncompressedBlock); | ||||
| 			uUncompressedBlockIndex = m_vecUncompressedBlockCache.size() - 1; | ||||
| 		} | ||||
|  | ||||
| 		block->uncompress(); | ||||
| 		//VoxelType* pData = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength]; | ||||
| 		//VoxelType* pData = &(m_pUncompressedBlockData[uUncompressedBlockIndex][0]); | ||||
| 		//VoxelType* pData = m_pUncompressedBlockData + (m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * uUncompressedBlockIndex); | ||||
| 		block->uncompress(m_vecUncompressedBlockCache[uUncompressedBlockIndex].data); | ||||
| 		m_uUncompressions++; | ||||
|  | ||||
| 		return block; | ||||
| 	} | ||||
|  | ||||
| 	template <typename VoxelType> | ||||
| 	uint32_t Volume<VoxelType>::sizeInChars(void) | ||||
| 	uint32_t Volume<VoxelType>::sizeInBytes(void) | ||||
| 	{ | ||||
| 		uint32_t uSizeInChars = 0; | ||||
| 		uint32_t uSizeInBytes = 0; | ||||
| 		for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) | ||||
| 		{ | ||||
| 			uSizeInChars += m_pBlocks[i].sizeInChars(); | ||||
| 			uSizeInBytes += m_pBlocks[i].sizeInBytes(); | ||||
| 		} | ||||
| 		return uSizeInChars; | ||||
| 		return uSizeInBytes; | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user