polyvox/tests/testvolume.cpp
2015-03-25 16:40:05 +01:00

517 lines
16 KiB
C++

/*******************************************************************************
Copyright (c) 2010 Matt Williams
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*******************************************************************************/
#include "testvolume.h"
#include "PolyVox/FilePager.h"
#include "PolyVox/PagedVolume.h"
#include "PolyVox/RawVolume.h"
#include <QtGlobal>
#include <QtTest>
#include <random>
using namespace PolyVox;
// This is used to compute a value from a list of integers. We use it to
// make sure we get the expected result from a series of volume accesses.
inline int32_t cantorTupleFunction(int32_t previousResult, int32_t value)
{
return (( previousResult + value ) * ( previousResult + value + 1 ) + value ) / 2;
}
/*
* Funtions for testing iteration in a forwards direction
*/
// We allow user provided offset in this function so we can test the case when all samples are inside a volume and also the case when some samples are outside.
// This is important because samplers are often slower when outside the volume as they have to fall back on directly accessing the volume data.
template <typename VolumeType>
int32_t testDirectAccessWithWrappingForwards(const VolumeType* volume, Region region)
{
int32_t result = 0;
for (int z = region.getLowerZ(); z <= region.getUpperZ(); z++)
{
for (int y = region.getLowerY(); y <= region.getUpperY(); y++)
{
for (int x = region.getLowerX(); x <= region.getUpperX(); x++)
{
//Three level loop now processes 27 voxel neighbourhood
for(int innerZ = -1; innerZ <=1; innerZ++)
{
for(int innerY = -1; innerY <=1; innerY++)
{
for(int innerX = -1; innerX <=1; innerX++)
{
result = cantorTupleFunction(result, volume->getVoxel(x + innerX, y + innerY, z + innerZ));
}
}
}
//End of inner loops
}
}
}
return result;
}
template <typename VolumeType>
int32_t testSamplersWithWrappingForwards(VolumeType* volume, Region region)
{
int32_t result = 0;
//Test the sampler move functions
typename VolumeType::Sampler xSampler(volume);
typename VolumeType::Sampler ySampler(volume);
typename VolumeType::Sampler zSampler(volume);
zSampler.setPosition(region.getLowerX(), region.getLowerY(), region.getLowerZ());
for (int z = region.getLowerZ(); z <= region.getUpperZ(); z++)
{
ySampler = zSampler;
for (int y = region.getLowerY(); y <= region.getUpperY(); y++)
{
xSampler = ySampler;
for (int x = region.getLowerX(); x <= region.getUpperX(); x++)
{
xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the PagedVolume.
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px0py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px0py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1pz());
xSampler.movePositiveX();
}
ySampler.movePositiveY();
}
zSampler.movePositiveZ();
}
return result;
}
/*
* Funtions for testing iteration in a backwards direction
*/
// We allow user provided offset in this function so we can test the case when all samples are inside a volume and also the case when some samples are outside.
// This is important because samplers are often slower when outside the volume as they have to fall back on directly accessing the volume data.
template <typename VolumeType>
int32_t testDirectAccessWithWrappingBackwards(const VolumeType* volume, Region region)
{
int32_t result = 0;
for (int z = region.getUpperZ(); z >= region.getLowerZ(); z--)
{
for (int y = region.getUpperY(); y >= region.getLowerY(); y--)
{
for (int x = region.getUpperX(); x >= region.getLowerX(); x--)
{
//Three level loop now processes 27 voxel neighbourhood
for(int innerZ = -1; innerZ <=1; innerZ++)
{
for(int innerY = -1; innerY <=1; innerY++)
{
for(int innerX = -1; innerX <=1; innerX++)
{
result = cantorTupleFunction(result, volume->getVoxel(x + innerX, y + innerY, z + innerZ));
}
}
}
//End of inner loops
}
}
}
return result;
}
template <typename VolumeType>
int32_t testSamplersWithWrappingBackwards(VolumeType* volume, Region region)
{
int32_t result = 0;
//Test the sampler move functions
typename VolumeType::Sampler xSampler(volume);
typename VolumeType::Sampler ySampler(volume);
typename VolumeType::Sampler zSampler(volume);
zSampler.setPosition(region.getUpperX(), region.getUpperY(), region.getUpperZ());
for (int z = region.getUpperZ(); z >= region.getLowerZ(); z--)
{
ySampler = zSampler;
for (int y = region.getUpperY(); y >= region.getLowerY(); y--)
{
xSampler = ySampler;
for (int x = region.getUpperX(); x >= region.getLowerX(); x--)
{
xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the PagedVolume.
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1nz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px0py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px0py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1py0pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1ny1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx0py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px0py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px0py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel0px1py1pz());
result = cantorTupleFunction(result, xSampler.peekVoxel1px1py1pz());
xSampler.moveNegativeX();
}
ySampler.moveNegativeY();
}
zSampler.moveNegativeZ();
}
return result;
}
TestVolume::TestVolume()
{
m_regVolume = Region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size
m_regInternal = m_regVolume;
m_regInternal.shiftLowerCorner(4, 2, 2);
m_regInternal.shiftUpperCorner(-3, -1, -2);
m_regExternal = m_regVolume;
m_regExternal.shiftLowerCorner(-1, -3, -2);
m_regExternal.shiftUpperCorner(2, 5, 4);
m_pFilePager = new FilePager<int32_t>(".");
//Create the volumes
m_pRawVolume = new RawVolume<int32_t>(m_regVolume);
m_pPagedVolume = new PagedVolume<int32_t>(m_pFilePager, 1 * 1024 * 1024, m_uChunkSideLength);
//Fill the volume with some data
for (int z = m_regVolume.getLowerZ(); z <= m_regVolume.getUpperZ(); z++)
{
for (int y = m_regVolume.getLowerY(); y <= m_regVolume.getUpperY(); y++)
{
for (int x = m_regVolume.getLowerX(); x <= m_regVolume.getUpperX(); x++)
{
int32_t value = x + y + z;
m_pRawVolume->setVoxel(x, y, z, value);
m_pPagedVolume->setVoxel(x, y, z, value);
}
}
}
// Note - We are reusing the FilePager for testing... watch out for conflicts with the main volume.
m_pPagedVolumeChunk = new PagedVolume<int32_t>::Chunk(Vector3DInt32(10000, 10000, 10000), m_uChunkSideLength, m_pFilePager);
int32_t i = 0;
for (uint16_t z = 0; z < m_uChunkSideLength; z++)
{
for (uint16_t y = 0; y < m_uChunkSideLength; y++)
{
for (uint16_t x = 0; x < m_uChunkSideLength; x++)
{
m_pPagedVolumeChunk->setVoxel(x, y, z, ++i);
}
}
}
}
TestVolume::~TestVolume()
{
delete m_pPagedVolumeChunk;
delete m_pRawVolume;
delete m_pPagedVolume;
delete m_pFilePager;
}
/*
* RawVolume Tests
*/
void TestVolume::testRawVolumeDirectAccessAllInternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingForwards(m_pRawVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(1004598054));
}
void TestVolume::testRawVolumeSamplersAllInternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingForwards(m_pRawVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(1004598054));
}
void TestVolume::testRawVolumeDirectAccessWithExternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingForwards(m_pRawVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(337227750));
}
void TestVolume::testRawVolumeSamplersWithExternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingForwards(m_pRawVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(337227750));
}
void TestVolume::testRawVolumeDirectAccessAllInternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingBackwards(m_pRawVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(-269366578));
}
void TestVolume::testRawVolumeSamplersAllInternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingBackwards(m_pRawVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(-269366578));
}
void TestVolume::testRawVolumeDirectAccessWithExternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingBackwards(m_pRawVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(-993539594));
}
void TestVolume::testRawVolumeSamplersWithExternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingBackwards(m_pRawVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(-993539594));
}
/*
* PagedVolume Tests
*/
void TestVolume::testPagedVolumeDirectAccessAllInternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingForwards(m_pPagedVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(1004598054));
}
void TestVolume::testPagedVolumeSamplersAllInternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingForwards(m_pPagedVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(1004598054));
}
void TestVolume::testPagedVolumeDirectAccessWithExternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingForwards(m_pPagedVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(337227750));
}
void TestVolume::testPagedVolumeSamplersWithExternalForwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingForwards(m_pPagedVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(337227750));
}
void TestVolume::testPagedVolumeDirectAccessAllInternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingBackwards(m_pPagedVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(-269366578));
}
void TestVolume::testPagedVolumeSamplersAllInternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingBackwards(m_pPagedVolume, m_regInternal);
}
QCOMPARE(result, static_cast<int32_t>(-269366578));
}
void TestVolume::testPagedVolumeDirectAccessWithExternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrappingBackwards(m_pPagedVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(-993539594));
}
void TestVolume::testPagedVolumeSamplersWithExternalBackwards()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrappingBackwards(m_pPagedVolume, m_regExternal);
}
QCOMPARE(result, static_cast<int32_t>(-993539594));
}
void TestVolume::testPagedVolumeChunkLocalAccess()
{
std::mt19937 rng;
int32_t result = 0;
uint16_t x = rng() % m_uChunkSideLength;
uint16_t y = rng() % m_uChunkSideLength;
uint16_t z = rng() % m_uChunkSideLength;
QBENCHMARK
{
for (uint32_t ct = 0; ct < 1000000; ct++)
{
uint16_t xOffset = rng() % 3;
uint16_t yOffset = rng() % 3;
uint16_t zOffset = rng() % 3;
x += xOffset;
y += yOffset;
z += zOffset;
x %= m_uChunkSideLength;
y %= m_uChunkSideLength;
z %= m_uChunkSideLength;
int32_t voxel = m_pPagedVolumeChunk->getVoxel(x, y, z);
result = cantorTupleFunction(result, voxel);
}
}
QCOMPARE(result, static_cast<int32_t>(145244783));
}
void TestVolume::testPagedVolumeChunkRandomAccess()
{
std::mt19937 rng;
int32_t result = 0;
QBENCHMARK
{
for (uint32_t ct = 0; ct < 1000000; ct++)
{
uint16_t x = rng() % m_uChunkSideLength;
uint16_t y = rng() % m_uChunkSideLength;
uint16_t z = rng() % m_uChunkSideLength;
int32_t voxel = m_pPagedVolumeChunk->getVoxel(x, y, z);
result = cantorTupleFunction(result, voxel);
}
}
QCOMPARE(result, static_cast<int32_t>(408757678));
}
QTEST_MAIN(TestVolume)