939 lines
30 KiB
C++
939 lines
30 KiB
C++
//============================================================================
|
|
// Copyright (c) Kitware, Inc.
|
|
// All rights reserved.
|
|
// See LICENSE.txt for details.
|
|
// This software is distributed WITHOUT ANY WARRANTY; without even
|
|
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
// PURPOSE. See the above copyright notice for more information.
|
|
//
|
|
// Copyright 2015 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
|
|
// Copyright 2015 UT-Battelle, LLC.
|
|
// Copyright 2015 Los Alamos National Security.
|
|
//
|
|
// Under the terms of Contract DE-NA0003525 with NTESS,
|
|
// the U.S. Government retains certain rights in this software.
|
|
//
|
|
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
|
|
// Laboratory (LANL), the U.S. Government retains certain rights in
|
|
// this software.
|
|
//============================================================================
|
|
|
|
#include <math.h>
|
|
|
|
#include <vtkm/Math.h>
|
|
#include <vtkm/VectorAnalysis.h>
|
|
|
|
#include <vtkm/cont/DeviceAdapter.h>
|
|
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
|
|
#include <vtkm/cont/RuntimeDeviceTracker.h>
|
|
#include <vtkm/cont/Timer.h>
|
|
#include <vtkm/cont/TryExecute.h>
|
|
|
|
#include <vtkm/exec/AtomicArray.h>
|
|
|
|
#include <vtkm/rendering/raytracing/BoundingVolumeHierarchy.h>
|
|
#include <vtkm/rendering/raytracing/Logger.h>
|
|
#include <vtkm/rendering/raytracing/MortonCodes.h>
|
|
#include <vtkm/rendering/raytracing/RayTracingTypeDefs.h>
|
|
#include <vtkm/rendering/raytracing/Worklets.h>
|
|
|
|
#include <vtkm/worklet/DispatcherMapField.h>
|
|
#include <vtkm/worklet/WorkletMapField.h>
|
|
|
|
#define AABB_EPSILON 0.00001f
|
|
namespace vtkm
|
|
{
|
|
namespace rendering
|
|
{
|
|
namespace raytracing
|
|
{
|
|
namespace detail
|
|
{
|
|
class LinearBVHBuilder
|
|
{
|
|
public:
|
|
class CountingIterator;
|
|
class FindAABBs;
|
|
|
|
template <typename Device>
|
|
class GatherFloat32;
|
|
|
|
template <typename Device>
|
|
class GatherVecCast;
|
|
|
|
class BVHData;
|
|
|
|
template <typename Device>
|
|
class PropagateAABBs;
|
|
|
|
template <typename Device>
|
|
class TreeBuilder;
|
|
|
|
VTKM_CONT
|
|
LinearBVHBuilder() {}
|
|
|
|
template <typename Device>
|
|
VTKM_CONT void SortAABBS(
|
|
BVHData& bvh,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>>& triangleIndices,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Int32, 4>>& outputTriangleIndices,
|
|
Device vtkmNotUsed(device));
|
|
|
|
template <typename Device>
|
|
VTKM_CONT void RunOnDevice(LinearBVH& linearBVH, Device device);
|
|
}; // class LinearBVHBuilder
|
|
|
|
class LinearBVHBuilder::CountingIterator : public vtkm::worklet::WorkletMapField
|
|
{
|
|
public:
|
|
VTKM_CONT
|
|
CountingIterator() {}
|
|
typedef void ControlSignature(FieldOut<>);
|
|
typedef void ExecutionSignature(WorkIndex, _1);
|
|
VTKM_EXEC
|
|
void operator()(const vtkm::Id& index, vtkm::Id& outId) const { outId = index; }
|
|
}; //class countingIterator
|
|
|
|
class LinearBVHBuilder::FindAABBs : public vtkm::worklet::WorkletMapField
|
|
{
|
|
public:
|
|
VTKM_CONT
|
|
FindAABBs() {}
|
|
typedef void ControlSignature(FieldIn<>,
|
|
FieldOut<>,
|
|
FieldOut<>,
|
|
FieldOut<>,
|
|
FieldOut<>,
|
|
FieldOut<>,
|
|
FieldOut<>,
|
|
WholeArrayIn<Vec3RenderingTypes>);
|
|
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8);
|
|
template <typename PointPortalType>
|
|
VTKM_EXEC void operator()(const vtkm::Vec<vtkm::Id, 4> indices,
|
|
vtkm::Float32& xmin,
|
|
vtkm::Float32& ymin,
|
|
vtkm::Float32& zmin,
|
|
vtkm::Float32& xmax,
|
|
vtkm::Float32& ymax,
|
|
vtkm::Float32& zmax,
|
|
const PointPortalType& points) const
|
|
{
|
|
// cast to Float32
|
|
vtkm::Vec<vtkm::Float32, 3> point;
|
|
point = static_cast<vtkm::Vec<vtkm::Float32, 3>>(points.Get(indices[1]));
|
|
xmin = point[0];
|
|
ymin = point[1];
|
|
zmin = point[2];
|
|
xmax = xmin;
|
|
ymax = ymin;
|
|
zmax = zmin;
|
|
point = static_cast<vtkm::Vec<vtkm::Float32, 3>>(points.Get(indices[2]));
|
|
xmin = vtkm::Min(xmin, point[0]);
|
|
ymin = vtkm::Min(ymin, point[1]);
|
|
zmin = vtkm::Min(zmin, point[2]);
|
|
xmax = vtkm::Max(xmax, point[0]);
|
|
ymax = vtkm::Max(ymax, point[1]);
|
|
zmax = vtkm::Max(zmax, point[2]);
|
|
point = static_cast<vtkm::Vec<vtkm::Float32, 3>>(points.Get(indices[3]));
|
|
xmin = vtkm::Min(xmin, point[0]);
|
|
ymin = vtkm::Min(ymin, point[1]);
|
|
zmin = vtkm::Min(zmin, point[2]);
|
|
xmax = vtkm::Max(xmax, point[0]);
|
|
ymax = vtkm::Max(ymax, point[1]);
|
|
zmax = vtkm::Max(zmax, point[2]);
|
|
|
|
|
|
vtkm::Float32 xEpsilon, yEpsilon, zEpsilon;
|
|
const vtkm::Float32 minEpsilon = 1e-6f;
|
|
xEpsilon = vtkm::Max(minEpsilon, AABB_EPSILON * (xmax - xmin));
|
|
yEpsilon = vtkm::Max(minEpsilon, AABB_EPSILON * (ymax - ymin));
|
|
zEpsilon = vtkm::Max(minEpsilon, AABB_EPSILON * (zmax - zmin));
|
|
|
|
xmin -= xEpsilon;
|
|
ymin -= yEpsilon;
|
|
zmin -= zEpsilon;
|
|
xmax += xEpsilon;
|
|
ymax += yEpsilon;
|
|
zmax += zEpsilon;
|
|
}
|
|
}; //class FindAABBs
|
|
|
|
template <typename Device>
|
|
class LinearBVHBuilder::GatherFloat32 : public vtkm::worklet::WorkletMapField
|
|
{
|
|
private:
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Float32> FloatArrayHandle;
|
|
typedef typename FloatArrayHandle::ExecutionTypes<Device>::PortalConst PortalConst;
|
|
typedef typename FloatArrayHandle::ExecutionTypes<Device>::Portal Portal;
|
|
PortalConst InputPortal;
|
|
Portal OutputPortal;
|
|
|
|
public:
|
|
VTKM_CONT
|
|
GatherFloat32(const FloatArrayHandle& inputPortal,
|
|
FloatArrayHandle& outputPortal,
|
|
const vtkm::Id& size)
|
|
: InputPortal(inputPortal.PrepareForInput(Device()))
|
|
{
|
|
this->OutputPortal = outputPortal.PrepareForOutput(size, Device());
|
|
}
|
|
typedef void ControlSignature(FieldIn<>);
|
|
typedef void ExecutionSignature(WorkIndex, _1);
|
|
VTKM_EXEC
|
|
void operator()(const vtkm::Id& outIndex, const vtkm::Id& inIndex) const
|
|
{
|
|
OutputPortal.Set(outIndex, InputPortal.Get(inIndex));
|
|
}
|
|
}; //class GatherFloat
|
|
|
|
template <typename Device>
|
|
class LinearBVHBuilder::GatherVecCast : public vtkm::worklet::WorkletMapField
|
|
{
|
|
private:
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>> Vec4IdArrayHandle;
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Int32, 4>> Vec4IntArrayHandle;
|
|
typedef typename Vec4IdArrayHandle::ExecutionTypes<Device>::PortalConst PortalConst;
|
|
typedef typename Vec4IntArrayHandle::ExecutionTypes<Device>::Portal Portal;
|
|
|
|
private:
|
|
PortalConst InputPortal;
|
|
Portal OutputPortal;
|
|
|
|
public:
|
|
VTKM_CONT
|
|
GatherVecCast(const Vec4IdArrayHandle& inputPortal,
|
|
Vec4IntArrayHandle& outputPortal,
|
|
const vtkm::Id& size)
|
|
: InputPortal(inputPortal.PrepareForInput(Device()))
|
|
{
|
|
this->OutputPortal = outputPortal.PrepareForOutput(size, Device());
|
|
}
|
|
typedef void ControlSignature(FieldIn<>);
|
|
typedef void ExecutionSignature(WorkIndex, _1);
|
|
VTKM_EXEC
|
|
void operator()(const vtkm::Id& outIndex, const vtkm::Id& inIndex) const
|
|
{
|
|
OutputPortal.Set(outIndex, InputPortal.Get(inIndex));
|
|
}
|
|
}; //class GatherVec3Id
|
|
|
|
class LinearBVHBuilder::BVHData
|
|
{
|
|
public:
|
|
//TODO: make private
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* xmins;
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* ymins;
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* zmins;
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* xmaxs;
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* ymaxs;
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* zmaxs;
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::UInt32> mortonCodes;
|
|
vtkm::cont::ArrayHandle<vtkm::Id> parent;
|
|
vtkm::cont::ArrayHandle<vtkm::Id> leftChild;
|
|
vtkm::cont::ArrayHandle<vtkm::Id> rightChild;
|
|
|
|
template <typename Device>
|
|
VTKM_CONT BVHData(vtkm::Id numPrimitives, Device vtkmNotUsed(device))
|
|
: NumPrimitives(numPrimitives)
|
|
{
|
|
InnerNodeCount = NumPrimitives - 1;
|
|
vtkm::Id size = NumPrimitives + InnerNodeCount;
|
|
xmins = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
ymins = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
zmins = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
xmaxs = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
ymaxs = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
zmaxs = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
|
|
parent.PrepareForOutput(size, Device());
|
|
leftChild.PrepareForOutput(InnerNodeCount, Device());
|
|
rightChild.PrepareForOutput(InnerNodeCount, Device());
|
|
mortonCodes.PrepareForOutput(NumPrimitives, Device());
|
|
}
|
|
|
|
VTKM_CONT
|
|
~BVHData()
|
|
{
|
|
//
|
|
delete xmins;
|
|
delete ymins;
|
|
delete zmins;
|
|
delete xmaxs;
|
|
delete ymaxs;
|
|
delete zmaxs;
|
|
}
|
|
VTKM_CONT
|
|
vtkm::Id GetNumberOfPrimitives() const { return NumPrimitives; }
|
|
VTKM_CONT
|
|
vtkm::Id GetNumberOfInnerNodes() const { return InnerNodeCount; }
|
|
|
|
private:
|
|
vtkm::Id NumPrimitives;
|
|
vtkm::Id InnerNodeCount;
|
|
|
|
}; // class BVH
|
|
|
|
template <typename Device>
|
|
class LinearBVHBuilder::PropagateAABBs : public vtkm::worklet::WorkletMapField
|
|
{
|
|
private:
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Id> IdArrayHandle;
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Int8> Int8Handle;
|
|
typedef typename vtkm::cont::ArrayHandle<Vec<vtkm::Float32, 2>> Float2ArrayHandle;
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Int32, 2>> VecInt2Handle;
|
|
typedef typename vtkm::cont::ArrayHandle<Vec<vtkm::Float32, 4>> Float4ArrayHandle;
|
|
|
|
typedef typename IdArrayHandle::ExecutionTypes<Device>::PortalConst IdConstPortal;
|
|
typedef typename Float2ArrayHandle::ExecutionTypes<Device>::Portal Float2ArrayPortal;
|
|
typedef typename VecInt2Handle::ExecutionTypes<Device>::Portal Int2ArrayPortal;
|
|
typedef typename Int8Handle::ExecutionTypes<Device>::Portal Int8ArrayPortal;
|
|
typedef typename Float4ArrayHandle::ExecutionTypes<Device>::Portal Float4ArrayPortal;
|
|
|
|
Float4ArrayPortal FlatBVH;
|
|
IdConstPortal Parents;
|
|
IdConstPortal LeftChildren;
|
|
IdConstPortal RightChildren;
|
|
vtkm::Int32 LeafCount;
|
|
//Int8Handle Counters;
|
|
//Int8ArrayPortal CountersPortal;
|
|
vtkm::exec::AtomicArray<vtkm::Int32, Device> Counters;
|
|
|
|
public:
|
|
VTKM_CONT
|
|
PropagateAABBs(IdArrayHandle& parents,
|
|
IdArrayHandle& leftChildren,
|
|
IdArrayHandle& rightChildren,
|
|
vtkm::Int32 leafCount,
|
|
Float4ArrayHandle flatBVH,
|
|
const vtkm::exec::AtomicArray<vtkm::Int32, Device>& counters)
|
|
: Parents(parents.PrepareForInput(Device()))
|
|
, LeftChildren(leftChildren.PrepareForInput(Device()))
|
|
, RightChildren(rightChildren.PrepareForInput(Device()))
|
|
, LeafCount(leafCount)
|
|
, Counters(counters)
|
|
|
|
{
|
|
this->FlatBVH = flatBVH.PrepareForOutput((LeafCount - 1) * 4, Device());
|
|
}
|
|
typedef void ControlSignature(WholeArrayIn<Scalar>,
|
|
WholeArrayIn<Scalar>,
|
|
WholeArrayIn<Scalar>,
|
|
WholeArrayIn<Scalar>,
|
|
WholeArrayIn<Scalar>,
|
|
WholeArrayIn<Scalar>);
|
|
typedef void ExecutionSignature(WorkIndex, _1, _2, _3, _4, _5, _6);
|
|
|
|
template <typename InputPortalType>
|
|
VTKM_EXEC_CONT void operator()(const vtkm::Id workIndex,
|
|
const InputPortalType& xmin,
|
|
const InputPortalType& ymin,
|
|
const InputPortalType& zmin,
|
|
const InputPortalType& xmax,
|
|
const InputPortalType& ymax,
|
|
const InputPortalType& zmax) const
|
|
{
|
|
//move up into the inner nodes
|
|
vtkm::Id currentNode = LeafCount - 1 + workIndex;
|
|
vtkm::Vec<vtkm::Id, 2> childVector;
|
|
while (currentNode != 0)
|
|
{
|
|
currentNode = Parents.Get(currentNode);
|
|
|
|
vtkm::Int32 oldCount = Counters.Add(currentNode, 1);
|
|
if (oldCount == 0)
|
|
return;
|
|
vtkm::Id currentNodeOffset = currentNode * 4;
|
|
childVector[0] = LeftChildren.Get(currentNode);
|
|
childVector[1] = RightChildren.Get(currentNode);
|
|
if (childVector[0] > (LeafCount - 2))
|
|
{
|
|
childVector[0] = childVector[0] - LeafCount + 1;
|
|
|
|
vtkm::Vec<vtkm::Float32, 4>
|
|
first4Vec; // = FlatBVH.Get(currentNode); only this one needs effects this
|
|
|
|
first4Vec[0] = xmin.Get(childVector[0]);
|
|
first4Vec[1] = ymin.Get(childVector[0]);
|
|
first4Vec[2] = zmin.Get(childVector[0]);
|
|
first4Vec[3] = xmax.Get(childVector[0]);
|
|
FlatBVH.Set(currentNodeOffset, first4Vec);
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> second4Vec = FlatBVH.Get(currentNodeOffset + 1);
|
|
second4Vec[0] = ymax.Get(childVector[0]);
|
|
second4Vec[1] = zmax.Get(childVector[0]);
|
|
FlatBVH.Set(currentNodeOffset + 1, second4Vec);
|
|
|
|
childVector[0] = -(childVector[0] + 1);
|
|
}
|
|
else
|
|
{
|
|
vtkm::Id child = childVector[0] * 4;
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> cFirst4Vec = FlatBVH.Get(child);
|
|
vtkm::Vec<vtkm::Float32, 4> cSecond4Vec = FlatBVH.Get(child + 1);
|
|
vtkm::Vec<vtkm::Float32, 4> cThird4Vec = FlatBVH.Get(child + 2);
|
|
|
|
cFirst4Vec[0] = vtkm::Min(cFirst4Vec[0], cSecond4Vec[2]);
|
|
cFirst4Vec[1] = vtkm::Min(cFirst4Vec[1], cSecond4Vec[3]);
|
|
cFirst4Vec[2] = vtkm::Min(cFirst4Vec[2], cThird4Vec[0]);
|
|
cFirst4Vec[3] = vtkm::Max(cFirst4Vec[3], cThird4Vec[1]);
|
|
FlatBVH.Set(currentNodeOffset, cFirst4Vec);
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> second4Vec = FlatBVH.Get(currentNodeOffset + 1);
|
|
second4Vec[0] = vtkm::Max(cSecond4Vec[0], cThird4Vec[2]);
|
|
second4Vec[1] = vtkm::Max(cSecond4Vec[1], cThird4Vec[3]);
|
|
|
|
FlatBVH.Set(currentNodeOffset + 1, second4Vec);
|
|
}
|
|
|
|
if (childVector[1] > (LeafCount - 2))
|
|
{
|
|
childVector[1] = childVector[1] - LeafCount + 1;
|
|
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> second4Vec = FlatBVH.Get(currentNodeOffset + 1);
|
|
|
|
second4Vec[2] = xmin.Get(childVector[1]);
|
|
second4Vec[3] = ymin.Get(childVector[1]);
|
|
FlatBVH.Set(currentNodeOffset + 1, second4Vec);
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> third4Vec;
|
|
third4Vec[0] = zmin.Get(childVector[1]);
|
|
third4Vec[1] = xmax.Get(childVector[1]);
|
|
third4Vec[2] = ymax.Get(childVector[1]);
|
|
third4Vec[3] = zmax.Get(childVector[1]);
|
|
FlatBVH.Set(currentNodeOffset + 2, third4Vec);
|
|
childVector[1] = -(childVector[1] + 1);
|
|
}
|
|
else
|
|
{
|
|
|
|
vtkm::Id child = childVector[1] * 4;
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> cFirst4Vec = FlatBVH.Get(child);
|
|
vtkm::Vec<vtkm::Float32, 4> cSecond4Vec = FlatBVH.Get(child + 1);
|
|
vtkm::Vec<vtkm::Float32, 4> cThird4Vec = FlatBVH.Get(child + 2);
|
|
|
|
vtkm::Vec<vtkm::Float32, 4> second4Vec = FlatBVH.Get(currentNodeOffset + 1);
|
|
second4Vec[2] = vtkm::Min(cFirst4Vec[0], cSecond4Vec[2]);
|
|
second4Vec[3] = vtkm::Min(cFirst4Vec[1], cSecond4Vec[3]);
|
|
FlatBVH.Set(currentNodeOffset + 1, second4Vec);
|
|
|
|
cThird4Vec[0] = vtkm::Min(cFirst4Vec[2], cThird4Vec[0]);
|
|
cThird4Vec[1] = vtkm::Max(cFirst4Vec[3], cThird4Vec[1]);
|
|
cThird4Vec[2] = vtkm::Max(cSecond4Vec[0], cThird4Vec[2]);
|
|
cThird4Vec[3] = vtkm::Max(cSecond4Vec[1], cThird4Vec[3]);
|
|
FlatBVH.Set(currentNodeOffset + 2, cThird4Vec);
|
|
}
|
|
vtkm::Vec<vtkm::Float32, 4> fourth4Vec;
|
|
vtkm::Int32 leftChild =
|
|
static_cast<vtkm::Int32>((childVector[0] >= 0) ? childVector[0] * 4 : childVector[0]);
|
|
memcpy(&fourth4Vec[0], &leftChild, 4);
|
|
vtkm::Int32 rightChild =
|
|
static_cast<vtkm::Int32>((childVector[1] >= 0) ? childVector[1] * 4 : childVector[1]);
|
|
memcpy(&fourth4Vec[1], &rightChild, 4);
|
|
FlatBVH.Set(currentNodeOffset + 3, fourth4Vec);
|
|
}
|
|
}
|
|
}; //class PropagateAABBs
|
|
|
|
|
|
template <typename Device>
|
|
class LinearBVHBuilder::TreeBuilder : public vtkm::worklet::WorkletMapField
|
|
{
|
|
public:
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::UInt32> UIntArrayHandle;
|
|
typedef typename vtkm::cont::ArrayHandle<vtkm::Id> IdArrayHandle;
|
|
typedef typename UIntArrayHandle::ExecutionTypes<Device>::PortalConst UIntPortalType;
|
|
typedef typename IdArrayHandle::ExecutionTypes<Device>::Portal IdPortalType;
|
|
|
|
private:
|
|
UIntPortalType MortonCodePortal;
|
|
IdPortalType ParentPortal;
|
|
vtkm::Id LeafCount;
|
|
vtkm::Id InnerCount;
|
|
//TODO: get instrinsic support
|
|
VTKM_EXEC
|
|
inline vtkm::Int32 CountLeadingZeros(vtkm::UInt32& x) const
|
|
{
|
|
vtkm::UInt32 y;
|
|
vtkm::UInt32 n = 32;
|
|
y = x >> 16;
|
|
if (y != 0)
|
|
{
|
|
n = n - 16;
|
|
x = y;
|
|
}
|
|
y = x >> 8;
|
|
if (y != 0)
|
|
{
|
|
n = n - 8;
|
|
x = y;
|
|
}
|
|
y = x >> 4;
|
|
if (y != 0)
|
|
{
|
|
n = n - 4;
|
|
x = y;
|
|
}
|
|
y = x >> 2;
|
|
if (y != 0)
|
|
{
|
|
n = n - 2;
|
|
x = y;
|
|
}
|
|
y = x >> 1;
|
|
if (y != 0)
|
|
return vtkm::Int32(n - 2);
|
|
return vtkm::Int32(n - x);
|
|
}
|
|
|
|
// returns the count of largest shared prefix between
|
|
// two morton codes. Ties are broken by the indexes
|
|
// a and b.
|
|
//
|
|
// returns count of the largest binary prefix
|
|
|
|
VTKM_EXEC
|
|
inline vtkm::Int32 delta(const vtkm::Int32& a, const vtkm::Int32& b) const
|
|
{
|
|
bool tie = false;
|
|
bool outOfRange = (b < 0 || b > LeafCount - 1);
|
|
//still make the call but with a valid adderss
|
|
vtkm::Int32 bb = (outOfRange) ? 0 : b;
|
|
vtkm::UInt32 aCode = MortonCodePortal.Get(a);
|
|
vtkm::UInt32 bCode = MortonCodePortal.Get(bb);
|
|
//use xor to find where they differ
|
|
vtkm::UInt32 exOr = aCode ^ bCode;
|
|
tie = (exOr == 0);
|
|
//break the tie, a and b must always differ
|
|
exOr = tie ? vtkm::UInt32(a) ^ vtkm::UInt32(bb) : exOr;
|
|
vtkm::Int32 count = CountLeadingZeros(exOr);
|
|
if (tie)
|
|
count += 32;
|
|
count = (outOfRange) ? -1 : count;
|
|
return count;
|
|
}
|
|
|
|
public:
|
|
VTKM_CONT
|
|
TreeBuilder(const UIntArrayHandle& mortonCodesHandle,
|
|
IdArrayHandle& parentHandle,
|
|
const vtkm::Id& leafCount)
|
|
: MortonCodePortal(mortonCodesHandle.PrepareForInput(Device()))
|
|
, LeafCount(leafCount)
|
|
{
|
|
InnerCount = LeafCount - 1;
|
|
this->ParentPortal = parentHandle.PrepareForOutput(InnerCount + LeafCount, Device());
|
|
}
|
|
typedef void ControlSignature(FieldOut<>, FieldOut<>);
|
|
typedef void ExecutionSignature(WorkIndex, _1, _2);
|
|
VTKM_EXEC
|
|
void operator()(const vtkm::Id& index, vtkm::Id& leftChild, vtkm::Id& rightChild) const
|
|
{
|
|
vtkm::Int32 idx = vtkm::Int32(index);
|
|
//something = MortonCodePortal.Get(index) + 1;
|
|
//determine range direction
|
|
vtkm::Int32 d = 0 > (delta(idx, idx + 1) - delta(idx, idx - 1)) ? -1 : 1;
|
|
|
|
//find upper bound for the length of the range
|
|
vtkm::Int32 minDelta = delta(idx, idx - d);
|
|
vtkm::Int32 lMax = 2;
|
|
while (delta(idx, idx + lMax * d) > minDelta)
|
|
lMax *= 2;
|
|
|
|
//binary search to find the lower bound
|
|
vtkm::Int32 l = 0;
|
|
for (int t = lMax / 2; t >= 1; t /= 2)
|
|
{
|
|
if (delta(idx, idx + (l + t) * d) > minDelta)
|
|
l += t;
|
|
}
|
|
|
|
vtkm::Int32 j = idx + l * d;
|
|
vtkm::Int32 deltaNode = delta(idx, j);
|
|
vtkm::Int32 s = 0;
|
|
vtkm::Float32 divFactor = 2.f;
|
|
//find the split postition using a binary search
|
|
for (vtkm::Int32 t = (vtkm::Int32)ceil(vtkm::Float32(l) / divFactor);;
|
|
divFactor *= 2, t = (vtkm::Int32)ceil(vtkm::Float32(l) / divFactor))
|
|
{
|
|
if (delta(idx, idx + (s + t) * d) > deltaNode)
|
|
{
|
|
s += t;
|
|
}
|
|
|
|
if (t == 1)
|
|
break;
|
|
}
|
|
|
|
vtkm::Int32 split = idx + s * d + vtkm::Min(d, 0);
|
|
//assign parent/child pointers
|
|
if (vtkm::Min(idx, j) == split)
|
|
{
|
|
//leaf
|
|
ParentPortal.Set(split + InnerCount, idx);
|
|
leftChild = split + InnerCount;
|
|
}
|
|
else
|
|
{
|
|
//inner node
|
|
ParentPortal.Set(split, idx);
|
|
leftChild = split;
|
|
}
|
|
|
|
|
|
if (vtkm::Max(idx, j) == split + 1)
|
|
{
|
|
//leaf
|
|
ParentPortal.Set(split + InnerCount + 1, idx);
|
|
rightChild = split + InnerCount + 1;
|
|
}
|
|
else
|
|
{
|
|
ParentPortal.Set(split + 1, idx);
|
|
rightChild = split + 1;
|
|
}
|
|
}
|
|
}; // class TreeBuilder
|
|
|
|
template <typename Device>
|
|
VTKM_CONT void LinearBVHBuilder::SortAABBS(
|
|
BVHData& bvh,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>>& triangleIndices,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Int32, 4>>& outputTriangleIndices,
|
|
Device vtkmNotUsed(device))
|
|
{
|
|
//create array of indexes to be sorted with morton codes
|
|
vtkm::cont::ArrayHandle<vtkm::Id> iterator;
|
|
iterator.PrepareForOutput(bvh.GetNumberOfPrimitives(), Device());
|
|
vtkm::worklet::DispatcherMapField<CountingIterator, Device> iteratorDispatcher;
|
|
iteratorDispatcher.Invoke(iterator);
|
|
|
|
//std::cout<<"\n\n\n";
|
|
//sort the morton codes
|
|
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::SortByKey(bvh.mortonCodes, iterator);
|
|
|
|
vtkm::Id arraySize = bvh.GetNumberOfPrimitives();
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* tempStorage;
|
|
vtkm::cont::ArrayHandle<vtkm::Float32>* tempPtr;
|
|
|
|
|
|
tempStorage = new vtkm::cont::ArrayHandle<vtkm::Float32>();
|
|
//xmins
|
|
vtkm::worklet::DispatcherMapField<GatherFloat32<Device>, Device>(
|
|
GatherFloat32<Device>(*bvh.xmins, *tempStorage, arraySize))
|
|
.Invoke(iterator);
|
|
|
|
tempPtr = bvh.xmins;
|
|
bvh.xmins = tempStorage;
|
|
tempStorage = tempPtr;
|
|
|
|
vtkm::worklet::DispatcherMapField<GatherFloat32<Device>, Device>(
|
|
GatherFloat32<Device>(*bvh.ymins, *tempStorage, arraySize))
|
|
.Invoke(iterator);
|
|
|
|
tempPtr = bvh.ymins;
|
|
bvh.ymins = tempStorage;
|
|
tempStorage = tempPtr;
|
|
//zmins
|
|
vtkm::worklet::DispatcherMapField<GatherFloat32<Device>, Device>(
|
|
GatherFloat32<Device>(*bvh.zmins, *tempStorage, arraySize))
|
|
.Invoke(iterator);
|
|
tempPtr = bvh.zmins;
|
|
bvh.zmins = tempStorage;
|
|
tempStorage = tempPtr;
|
|
//xmaxs
|
|
vtkm::worklet::DispatcherMapField<GatherFloat32<Device>, Device>(
|
|
GatherFloat32<Device>(*bvh.xmaxs, *tempStorage, arraySize))
|
|
.Invoke(iterator);
|
|
|
|
tempPtr = bvh.xmaxs;
|
|
bvh.xmaxs = tempStorage;
|
|
tempStorage = tempPtr;
|
|
//ymaxs
|
|
vtkm::worklet::DispatcherMapField<GatherFloat32<Device>, Device>(
|
|
GatherFloat32<Device>(*bvh.ymaxs, *tempStorage, arraySize))
|
|
.Invoke(iterator);
|
|
|
|
tempPtr = bvh.ymaxs;
|
|
bvh.ymaxs = tempStorage;
|
|
tempStorage = tempPtr;
|
|
//zmaxs
|
|
vtkm::worklet::DispatcherMapField<GatherFloat32<Device>, Device>(
|
|
GatherFloat32<Device>(*bvh.zmaxs, *tempStorage, arraySize))
|
|
.Invoke(iterator);
|
|
|
|
tempPtr = bvh.zmaxs;
|
|
bvh.zmaxs = tempStorage;
|
|
tempStorage = tempPtr;
|
|
|
|
vtkm::worklet::DispatcherMapField<GatherVecCast<Device>, Device>(
|
|
GatherVecCast<Device>(triangleIndices, outputTriangleIndices, arraySize))
|
|
.Invoke(iterator);
|
|
delete tempStorage;
|
|
|
|
} // method SortAABBs
|
|
|
|
// Adding this as a template parameter to allow restricted types and
|
|
// storage for dynamic coordinate system to limit crazy code bloat and
|
|
// compile times.
|
|
//
|
|
template <typename Device>
|
|
VTKM_CONT void LinearBVHBuilder::RunOnDevice(LinearBVH& linearBVH, Device device)
|
|
{
|
|
Logger* logger = Logger::GetInstance();
|
|
logger->OpenLogEntry("bvh_constuct");
|
|
logger->AddLogData("device", GetDeviceString(Device()));
|
|
vtkm::cont::Timer<Device> constructTimer;
|
|
|
|
auto coordsHandle = linearBVH.GetCoordsHandle();
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>> triangleIndices = linearBVH.GetTriangles();
|
|
vtkm::Id numberOfTriangles = linearBVH.GetNumberOfTriangles();
|
|
|
|
logger->AddLogData("bvh_num_triangles ", numberOfTriangles);
|
|
|
|
const vtkm::Id numBBoxes = numberOfTriangles;
|
|
BVHData bvh(numBBoxes, device);
|
|
|
|
vtkm::cont::Timer<Device> timer;
|
|
vtkm::worklet::DispatcherMapField<FindAABBs, Device>(FindAABBs())
|
|
.Invoke(triangleIndices,
|
|
*bvh.xmins,
|
|
*bvh.ymins,
|
|
*bvh.zmins,
|
|
*bvh.xmaxs,
|
|
*bvh.ymaxs,
|
|
*bvh.zmaxs,
|
|
coordsHandle);
|
|
|
|
vtkm::Float64 time = timer.GetElapsedTime();
|
|
logger->AddLogData("find_aabb", time);
|
|
timer.Reset();
|
|
|
|
// Find the extent of all bounding boxes to generate normalization for morton codes
|
|
vtkm::Vec<vtkm::Float32, 3> minExtent(vtkm::Infinity32(), vtkm::Infinity32(), vtkm::Infinity32());
|
|
vtkm::Vec<vtkm::Float32, 3> maxExtent(
|
|
vtkm::NegativeInfinity32(), vtkm::NegativeInfinity32(), vtkm::NegativeInfinity32());
|
|
maxExtent[0] =
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::Reduce(*bvh.xmaxs, maxExtent[0], MaxValue());
|
|
maxExtent[1] =
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::Reduce(*bvh.ymaxs, maxExtent[1], MaxValue());
|
|
maxExtent[2] =
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::Reduce(*bvh.zmaxs, maxExtent[2], MaxValue());
|
|
minExtent[0] =
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::Reduce(*bvh.xmins, minExtent[0], MinValue());
|
|
minExtent[1] =
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::Reduce(*bvh.ymins, minExtent[1], MinValue());
|
|
minExtent[2] =
|
|
vtkm::cont::DeviceAdapterAlgorithm<Device>::Reduce(*bvh.zmins, minExtent[2], MinValue());
|
|
|
|
time = timer.GetElapsedTime();
|
|
logger->AddLogData("calc_extents", time);
|
|
timer.Reset();
|
|
|
|
vtkm::Vec<vtkm::Float32, 3> deltaExtent = maxExtent - minExtent;
|
|
vtkm::Vec<vtkm::Float32, 3> inverseExtent;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
inverseExtent[i] = (deltaExtent[i] == 0.f) ? 0 : 1.f / deltaExtent[i];
|
|
}
|
|
|
|
//Generate the morton codes
|
|
vtkm::worklet::DispatcherMapField<MortonCodeAABB, Device>(
|
|
MortonCodeAABB(inverseExtent, minExtent))
|
|
.Invoke(
|
|
*bvh.xmins, *bvh.ymins, *bvh.zmins, *bvh.xmaxs, *bvh.ymaxs, *bvh.zmaxs, bvh.mortonCodes);
|
|
|
|
time = timer.GetElapsedTime();
|
|
logger->AddLogData("morton_codes", time);
|
|
timer.Reset();
|
|
|
|
linearBVH.Allocate(bvh.GetNumberOfPrimitives(), Device());
|
|
|
|
SortAABBS(bvh, triangleIndices, linearBVH.LeafNodes, Device());
|
|
|
|
time = timer.GetElapsedTime();
|
|
logger->AddLogData("sort_aabbs", time);
|
|
timer.Reset();
|
|
|
|
vtkm::worklet::DispatcherMapField<TreeBuilder<Device>, Device>(
|
|
TreeBuilder<Device>(bvh.mortonCodes, bvh.parent, bvh.GetNumberOfPrimitives()))
|
|
.Invoke(bvh.leftChild, bvh.rightChild);
|
|
|
|
time = timer.GetElapsedTime();
|
|
logger->AddLogData("build_tree", time);
|
|
timer.Reset();
|
|
|
|
const vtkm::Int32 primitiveCount = vtkm::Int32(bvh.GetNumberOfPrimitives());
|
|
|
|
vtkm::cont::ArrayHandle<vtkm::Int32> counters;
|
|
counters.PrepareForOutput(bvh.GetNumberOfPrimitives() - 1, Device());
|
|
vtkm::Int32 zero = 0;
|
|
vtkm::worklet::DispatcherMapField<MemSet<vtkm::Int32>, Device>(MemSet<vtkm::Int32>(zero))
|
|
.Invoke(counters);
|
|
vtkm::exec::AtomicArray<vtkm::Int32, Device> atomicCounters(counters);
|
|
|
|
|
|
vtkm::worklet::DispatcherMapField<PropagateAABBs<Device>, Device>(
|
|
PropagateAABBs<Device>(
|
|
bvh.parent, bvh.leftChild, bvh.rightChild, primitiveCount, linearBVH.FlatBVH, atomicCounters))
|
|
.Invoke(*bvh.xmins, *bvh.ymins, *bvh.zmins, *bvh.xmaxs, *bvh.ymaxs, *bvh.zmaxs);
|
|
|
|
time = timer.GetElapsedTime();
|
|
logger->AddLogData("propagate_aabbs", time);
|
|
|
|
time = constructTimer.GetElapsedTime();
|
|
logger->CloseLogEntry(time);
|
|
}
|
|
} //namespace detail
|
|
|
|
struct LinearBVH::ConstructFunctor
|
|
{
|
|
LinearBVH* Self;
|
|
VTKM_CONT
|
|
ConstructFunctor(LinearBVH* self)
|
|
: Self(self)
|
|
{
|
|
}
|
|
template <typename Device>
|
|
bool operator()(Device)
|
|
{
|
|
Self->ConstructOnDevice(Device());
|
|
return true;
|
|
}
|
|
};
|
|
|
|
LinearBVH::LinearBVH()
|
|
: IsConstructed(false)
|
|
, CanConstruct(false){};
|
|
|
|
VTKM_CONT
|
|
LinearBVH::LinearBVH(vtkm::cont::ArrayHandleVirtualCoordinates coordsHandle,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>> triangles,
|
|
vtkm::Bounds coordBounds)
|
|
: CoordBounds(coordBounds)
|
|
, CoordsHandle(coordsHandle)
|
|
, Triangles(triangles)
|
|
, IsConstructed(false)
|
|
, CanConstruct(true)
|
|
{
|
|
}
|
|
|
|
VTKM_CONT
|
|
LinearBVH::LinearBVH(const LinearBVH& other)
|
|
: FlatBVH(other.FlatBVH)
|
|
, LeafNodes(other.LeafNodes)
|
|
, LeafCount(other.LeafCount)
|
|
, CoordBounds(other.CoordBounds)
|
|
, CoordsHandle(other.CoordsHandle)
|
|
, Triangles(other.Triangles)
|
|
, IsConstructed(other.IsConstructed)
|
|
, CanConstruct(other.CanConstruct)
|
|
{
|
|
}
|
|
template <typename Device>
|
|
VTKM_CONT void LinearBVH::Allocate(const vtkm::Id& leafCount, Device deviceAdapter)
|
|
{
|
|
LeafCount = leafCount;
|
|
LeafNodes.PrepareForOutput(leafCount, deviceAdapter);
|
|
FlatBVH.PrepareForOutput((leafCount - 1) * 4, deviceAdapter);
|
|
}
|
|
|
|
void LinearBVH::Construct()
|
|
{
|
|
if (IsConstructed)
|
|
return;
|
|
if (!CanConstruct)
|
|
throw vtkm::cont::ErrorBadValue(
|
|
"Linear BVH: coordinates and triangles must be set before calling construct!");
|
|
|
|
ConstructFunctor functor(this);
|
|
vtkm::cont::TryExecute(functor);
|
|
IsConstructed = true;
|
|
}
|
|
|
|
VTKM_CONT
|
|
void LinearBVH::SetData(vtkm::cont::ArrayHandleVirtualCoordinates coordsHandle,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>> triangles,
|
|
vtkm::Bounds coordBounds)
|
|
{
|
|
CoordBounds = coordBounds;
|
|
CoordsHandle = coordsHandle;
|
|
Triangles = triangles;
|
|
IsConstructed = false;
|
|
CanConstruct = true;
|
|
}
|
|
|
|
template <typename Device>
|
|
void LinearBVH::ConstructOnDevice(Device device)
|
|
{
|
|
Logger* logger = Logger::GetInstance();
|
|
vtkm::cont::Timer<Device> timer;
|
|
logger->OpenLogEntry("bvh");
|
|
if (!CanConstruct)
|
|
throw vtkm::cont::ErrorBadValue(
|
|
"Linear BVH: coordinates and triangles must be set before calling construct!");
|
|
if (!IsConstructed)
|
|
{
|
|
//
|
|
// This algorithm needs at least 2 triangles
|
|
//
|
|
vtkm::Id numTriangles = this->GetNumberOfTriangles();
|
|
if (numTriangles == 1)
|
|
{
|
|
vtkm::Vec<vtkm::Id, 4> triangle = Triangles.GetPortalControl().Get(0);
|
|
Triangles.Allocate(2);
|
|
Triangles.GetPortalControl().Set(0, triangle);
|
|
Triangles.GetPortalControl().Set(1, triangle);
|
|
}
|
|
detail::LinearBVHBuilder builder;
|
|
builder.RunOnDevice(*this, device);
|
|
IsConstructed = true;
|
|
}
|
|
|
|
vtkm::Float64 time = timer.GetElapsedTime();
|
|
logger->CloseLogEntry(time);
|
|
}
|
|
|
|
// explicitly export to workaround an intel compiler bug
|
|
#if defined(VTKM_ICC)
|
|
template VTKM_CONT_EXPORT void LinearBVH::ConstructOnDevice<vtkm::cont::DeviceAdapterTagSerial>(
|
|
vtkm::cont::DeviceAdapterTagSerial);
|
|
#ifdef VTKM_ENABLE_TBB
|
|
template VTKM_CONT_EXPORT void LinearBVH::ConstructOnDevice<vtkm::cont::DeviceAdapterTagTBB>(
|
|
vtkm::cont::DeviceAdapterTagTBB);
|
|
#endif
|
|
#ifdef VTKM_ENABLE_CUDA
|
|
template VTKM_CONT_EXPORT void LinearBVH::ConstructOnDevice<vtkm::cont::DeviceAdapterTagCuda>(
|
|
vtkm::cont::DeviceAdapterTagCuda);
|
|
#endif
|
|
#endif
|
|
|
|
VTKM_CONT
|
|
bool LinearBVH::GetIsConstructed() const
|
|
{
|
|
return IsConstructed;
|
|
}
|
|
VTKM_CONT
|
|
vtkm::cont::ArrayHandleVirtualCoordinates LinearBVH::GetCoordsHandle() const
|
|
{
|
|
return CoordsHandle;
|
|
}
|
|
|
|
VTKM_CONT
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Id, 4>> LinearBVH::GetTriangles() const
|
|
{
|
|
return Triangles;
|
|
}
|
|
|
|
vtkm::Id LinearBVH::GetNumberOfTriangles() const
|
|
{
|
|
return Triangles.GetPortalConstControl().GetNumberOfValues();
|
|
}
|
|
}
|
|
}
|
|
} // namespace vtkm::rendering::raytracing
|