Merge topic 'fix-475-support-sg-3d-scheduling'

e2c32ffac add unit test for WorkletMapTopology
0e90c22e7 Worklet{MapTopology,PointNeighbor} custom sg/mask

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Robert Maynard <robert.maynard@kitware.com>
Merge-request: !1980
This commit is contained in:
Vicente Bolea 2020-03-19 15:16:59 +00:00 committed by Kitware Robot
commit 35276434bd
7 changed files with 399 additions and 35 deletions

@ -53,6 +53,7 @@ public:
//this->CellShape = connectivity.GetCellShape(index);
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC
ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D,
@ -60,9 +61,7 @@ public:
const ConnectivityType& connectivity,
vtkm::Id globalThreadIndexOffset = 0)
{
// We currently only support multidimensional indices on one-to-one input-
// to-output mappings. (We don't have a use case otherwise.)
// That is why we treat teh threadIndex as also the inputIndex and outputIndex
// This constructor handles multidimensional indices on one-to-one input-to-output
auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType());
this->ThreadIndex = threadIndex1D;
@ -75,6 +74,29 @@ public:
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC
ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D,
vtkm::Id threadIndex1D,
vtkm::Id inputIndex,
vtkm::IdComponent visitIndex,
vtkm::Id outputIndex,
const ConnectivityType& connectivity,
vtkm::Id globalThreadIndexOffset = 0)
{
// This constructor handles multidimensional indices on many-to-many input-to-output
auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType());
this->ThreadIndex = threadIndex1D;
this->InputIndex = inputIndex;
this->OutputIndex = outputIndex;
this->VisitIndex = visitIndex;
this->LogicalIndex = logicalIndex;
this->IndicesIncident = connectivity.GetIndices(logicalIndex);
//this->CellShape = connectivity.GetCellShape(index);
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
/// \brief The logical index into the input domain.
///
/// This is similar to \c GetIndex3D except the Vec size matches the actual
@ -159,7 +181,6 @@ private:
vtkm::Id OutputIndex;
LogicalIndexType LogicalIndex;
IndicesIncidentType IndicesIncident;
//CellShapeTag CellShape;
vtkm::Id GlobalThreadIndexOffset;
};
@ -192,7 +213,6 @@ public:
this->VisitIndex = 0;
this->LogicalIndex = logicalIndex;
this->IndicesIncident = connectivity.GetIndices(logicalIndex);
//this->CellShape = connectivity.GetCellShape(index);
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
@ -210,9 +230,29 @@ public:
this->VisitIndex = 0;
this->LogicalIndex = logicalIndex;
this->IndicesIncident = connectivity.GetIndices(logicalIndex);
//this->CellShape = connectivity.GetCellShape(index);
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D,
vtkm::Id threadIndex1D,
vtkm::Id inputIndex,
vtkm::IdComponent visitIndex,
vtkm::Id outputIndex,
const ConnectivityType& connectivity,
vtkm::Id globalThreadIndexOffset = 0)
{
const LogicalIndexType logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType());
this->ThreadIndex = threadIndex1D;
this->InputIndex = inputIndex;
this->OutputIndex = outputIndex;
this->VisitIndex = visitIndex;
this->LogicalIndex = logicalIndex;
this->IndicesIncident = connectivity.GetIndices(logicalIndex);
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
/// \brief The logical index into the input domain.
///
/// This is similar to \c GetIndex3D except the Vec size matches the actual
@ -297,7 +337,6 @@ private:
vtkm::Id OutputIndex;
LogicalIndexType LogicalIndex;
IndicesIncidentType IndicesIncident;
//CellShapeTag CellShape;
vtkm::Id GlobalThreadIndexOffset;
};

@ -72,6 +72,26 @@ public:
{
}
template <vtkm::IdComponent Dimension>
VTKM_EXEC ThreadIndicesPointNeighborhood(
const vtkm::Id3& threadIndex3D,
vtkm::Id threadIndex1D,
vtkm::Id inputIndex,
vtkm::IdComponent visitIndex,
vtkm::Id outputIndex,
const vtkm::exec::ConnectivityStructured<vtkm::TopologyElementTagPoint,
vtkm::TopologyElementTagCell,
Dimension>& connectivity,
vtkm::Id globalThreadIndexOffset = 0)
: State(threadIndex3D, detail::To3D(connectivity.GetPointDimensions()))
, ThreadIndex(threadIndex1D)
, InputIndex(inputIndex)
, OutputIndex(outputIndex)
, VisitIndex(visitIndex)
, GlobalThreadIndexOffset(globalThreadIndexOffset)
{
}
template <vtkm::IdComponent Dimension>
VTKM_EXEC ThreadIndicesPointNeighborhood(
vtkm::Id threadIndex,

@ -179,9 +179,7 @@ public:
const ConnectivityType& connectivity,
const vtkm::Id globalThreadIndexOffset = 0)
{
// We currently only support multidimensional indices on one-to-one input-
// to-output mappings. (We don't have a use case otherwise.)
// That is why we treat teh threadIndex as also the inputIndex and outputIndex
// This constructor handles multidimensional indices on one-to-one input-to-output
auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType());
this->ThreadIndex = threadIndex1D;
this->InputIndex = threadIndex1D;
@ -193,6 +191,26 @@ public:
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
VTKM_EXEC ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D,
vtkm::Id threadIndex1D,
vtkm::Id inIndex,
vtkm::IdComponent visitIndex,
vtkm::Id outIndex,
const ConnectivityType& connectivity,
const vtkm::Id globalThreadIndexOffset = 0)
{
// This constructor handles multidimensional indices on many-to-many input-to-output
auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType());
this->ThreadIndex = threadIndex1D;
this->InputIndex = inIndex;
this->VisitIndex = visitIndex;
this->OutputIndex = outIndex;
this->LogicalIndex = logicalIndex;
this->IndicesIncident = connectivity.GetIndices(logicalIndex);
this->CellShape = connectivity.GetCellShape(threadIndex1D);
this->GlobalThreadIndexOffset = globalThreadIndexOffset;
}
/// \brief The index of the thread or work invocation.
///
/// This index refers to which instance of the worklet is being invoked. Every invocation of the

@ -179,30 +179,68 @@ public:
globalThreadIndexOffset);
}
/// In the remaining methods and `constexpr` we determine at compilation time
/// which method definition will be actually used for GetThreadIndices.
///
/// We want to avoid further function calls when we use WorkletMapTopology in which
/// ScatterType is set as ScatterIdentity and MaskType as MaskNone.
/// Otherwise, we call the default method defined at the bottom of this class.
private:
static constexpr bool IsScatterIdentity =
std::is_same<ScatterType, vtkm::worklet::ScatterIdentity>::value;
static constexpr bool IsMaskNone = std::is_same<MaskType, vtkm::worklet::MaskNone>::value;
template <bool Cond, typename ReturnType>
using EnableFnWhen = typename std::enable_if<Cond, ReturnType>::type;
public:
/// Optimized for ScatterIdentity and MaskNone
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename OutToInArrayType,
typename VisitArrayType,
typename ThreadToOutArrayType,
typename InputDomainType>
VTKM_EXEC vtkm::exec::arg::ThreadIndicesTopologyMap<InputDomainType> GetThreadIndices(
vtkm::Id threadIndex1D,
const vtkm::Id3& threadIndex3D,
const OutToInArrayType& vtkmNotUsed(outToIn),
const VisitArrayType& vtkmNotUsed(visit),
const ThreadToOutArrayType& vtkmNotUsed(threadToOut),
const InputDomainType& connectivity,
vtkm::Id globalThreadIndexOffset = 0) const
typename InputDomainType,
bool S = IsScatterIdentity,
bool M = IsMaskNone>
VTKM_EXEC EnableFnWhen<S && M, vtkm::exec::arg::ThreadIndicesTopologyMap<InputDomainType>>
GetThreadIndices(vtkm::Id threadIndex1D,
const vtkm::Id3& threadIndex3D,
const OutToInArrayType& vtkmNotUsed(outToIn),
const VisitArrayType& vtkmNotUsed(visit),
const ThreadToOutArrayType& vtkmNotUsed(threadToOut),
const InputDomainType& connectivity,
vtkm::Id globalThreadIndexOffset = 0) const
{
using ScatterCheck = std::is_same<ScatterType, vtkm::worklet::ScatterIdentity>;
VTKM_STATIC_ASSERT_MSG(ScatterCheck::value,
"Scheduling on 3D topologies only works with default ScatterIdentity.");
using MaskCheck = std::is_same<MaskType, vtkm::worklet::MaskNone>;
VTKM_STATIC_ASSERT_MSG(MaskCheck::value,
"Scheduling on 3D topologies only works with default MaskNone.");
return vtkm::exec::arg::ThreadIndicesTopologyMap<InputDomainType>(
threadIndex3D, threadIndex1D, connectivity, globalThreadIndexOffset);
}
/// Default version
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename OutToInArrayType,
typename VisitArrayType,
typename ThreadToOutArrayType,
typename InputDomainType,
bool S = IsScatterIdentity,
bool M = IsMaskNone>
VTKM_EXEC EnableFnWhen<!(S && M), vtkm::exec::arg::ThreadIndicesTopologyMap<InputDomainType>>
GetThreadIndices(vtkm::Id threadIndex1D,
const vtkm::Id3& threadIndex3D,
const OutToInArrayType& outToIn,
const VisitArrayType& visit,
const ThreadToOutArrayType& threadToOut,
const InputDomainType& connectivity,
vtkm::Id globalThreadIndexOffset = 0) const
{
const vtkm::Id outIndex = threadToOut.Get(threadIndex1D);
return vtkm::exec::arg::ThreadIndicesTopologyMap<InputDomainType>(threadIndex3D,
threadIndex1D,
outToIn.Get(outIndex),
visit.Get(outIndex),
outIndex,
connectivity,
globalThreadIndexOffset);
}
};
/// Base class for worklets that map from Points to Cells.

@ -197,12 +197,30 @@ public:
globalThreadIndexOffset);
}
/// In the remaining methods and `constexpr` we determine at compilation time
/// which method definition will be actually used for GetThreadIndices.
///
/// We want to avoid further function calls when we use WorkletMapTopology in which
/// ScatterType is set as ScatterIdentity and MaskType as MaskNone.
/// Otherwise, we call the default method defined at the bottom of this class.
private:
static constexpr bool IsScatterIdentity =
std::is_same<ScatterType, vtkm::worklet::ScatterIdentity>::value;
static constexpr bool IsMaskNone = std::is_same<MaskType, vtkm::worklet::MaskNone>::value;
public:
template <bool Cond, typename ReturnType>
using EnableFnWhen = typename std::enable_if<Cond, ReturnType>::type;
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename OutToInArrayType,
typename VisitArrayType,
typename ThreadToOutArrayType,
typename InputDomainType>
VTKM_EXEC vtkm::exec::arg::ThreadIndicesPointNeighborhood GetThreadIndices(
typename InputDomainType,
bool S = IsScatterIdentity,
bool M = IsMaskNone>
VTKM_EXEC EnableFnWhen<S && M, vtkm::exec::arg::ThreadIndicesPointNeighborhood> GetThreadIndices(
vtkm::Id threadIndex1D,
const vtkm::Id3& threadIndex3D,
const OutToInArrayType& vtkmNotUsed(outToIn),
@ -211,16 +229,35 @@ public:
const InputDomainType& connectivity,
vtkm::Id globalThreadIndexOffset = 0) const
{
using ScatterCheck = std::is_same<ScatterType, vtkm::worklet::ScatterIdentity>;
VTKM_STATIC_ASSERT_MSG(ScatterCheck::value,
"Scheduling on 3D topologies only works with default ScatterIdentity.");
using MaskCheck = std::is_same<MaskType, vtkm::worklet::MaskNone>;
VTKM_STATIC_ASSERT_MSG(MaskCheck::value,
"Scheduling on 3D topologies only works with default MaskNone.");
return vtkm::exec::arg::ThreadIndicesPointNeighborhood(
threadIndex3D, threadIndex1D, connectivity, globalThreadIndexOffset);
}
VTKM_SUPPRESS_EXEC_WARNINGS
template <typename OutToInArrayType,
typename VisitArrayType,
typename ThreadToOutArrayType,
typename InputDomainType,
bool S = IsScatterIdentity,
bool M = IsMaskNone>
VTKM_EXEC EnableFnWhen<!(S && M), vtkm::exec::arg::ThreadIndicesPointNeighborhood>
GetThreadIndices(vtkm::Id threadIndex1D,
const vtkm::Id3& threadIndex3D,
const OutToInArrayType& outToIn,
const VisitArrayType& visit,
const ThreadToOutArrayType& threadToOut,
const InputDomainType& connectivity,
vtkm::Id globalThreadIndexOffset = 0) const
{
const vtkm::Id outIndex = threadToOut.Get(threadIndex1D);
return vtkm::exec::arg::ThreadIndicesPointNeighborhood(threadIndex3D,
threadIndex1D,
outToIn.Get(outIndex),
visit.Get(outIndex),
outIndex,
connectivity,
globalThreadIndexOffset);
}
};
}
}

@ -58,6 +58,7 @@ set(unit_tests
UnitTestScatterPermutation.cxx
UnitTestSplatKernels.cxx
UnitTestSplitSharpEdges.cxx
UnitTestScatterAndMaskWithTopology.cxx
UnitTestStreamingSine.cxx
UnitTestStreamLineUniformGrid.cxx
UnitTestStreamSurface.cxx

@ -0,0 +1,211 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/testing/MakeTestDataSet.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/WorkletMapTopology.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/worklet/MaskSelect.h>
#include <vtkm/worklet/ScatterUniform.h>
namespace maptopology3d
{
class TestWorkletMapTopo : public vtkm::worklet::WorkletVisitPointsWithCells
{
public:
using ControlSignature = void(CellSetIn topology, FieldInVisit pointCoords);
using ExecutionSignature = void(_2, WorkIndex, InputIndex, OutputIndex, VisitIndex);
virtual VTKM_EXEC void operator()(const vtkm::Vec<int, 3>& vtkmNotUsed(coords),
const vtkm::Id& workIndex,
const vtkm::Id& inputIndex,
const vtkm::Id& outputIndex,
const vtkm::Id& visitIndex) const = 0;
};
class TestWorkletMapTopoIdentity : public TestWorkletMapTopo
{
public:
using ScatterType = vtkm::worklet::ScatterIdentity;
VTKM_EXEC void operator()(const vtkm::Vec<int, 3>& vtkmNotUsed(coords),
const vtkm::Id& workIndex,
const vtkm::Id& inputIndex,
const vtkm::Id& outputIndex,
const vtkm::Id& visitIndex) const override
{
if (workIndex != inputIndex)
{
this->RaiseError("Got wrong input value.");
}
if (outputIndex != workIndex)
{
this->RaiseError("Got work and output index don't match.");
}
if (visitIndex != 0)
{
this->RaiseError("Got wrong visit value.");
}
}
};
class TestWorkletMapTopoUniform : public TestWorkletMapTopo
{
public:
using ScatterType = vtkm::worklet::ScatterUniform<2>;
VTKM_EXEC void operator()(const vtkm::Vec<int, 3>& vtkmNotUsed(coords),
const vtkm::Id& workIndex,
const vtkm::Id& inputIndex,
const vtkm::Id& outputIndex,
const vtkm::Id& visitIndex) const override
{
if ((workIndex / 2) != inputIndex)
{
this->RaiseError("Got wrong input value.");
}
if (outputIndex != workIndex)
{
this->RaiseError("Got work and output index don't match.");
}
if ((workIndex % 2) != visitIndex)
{
this->RaiseError("Got wrong visit value.");
}
}
};
class TestWorkletMapTopoNone : public TestWorkletMapTopo
{
public:
using MaskType = vtkm::worklet::MaskNone;
VTKM_EXEC void operator()(const vtkm::Vec<int, 3>& vtkmNotUsed(coords),
const vtkm::Id& workIndex,
const vtkm::Id& inputIndex,
const vtkm::Id& outputIndex,
const vtkm::Id& visitIndex) const override
{
if (workIndex != inputIndex)
{
this->RaiseError("Got wrong input value.");
}
if (outputIndex != workIndex)
{
this->RaiseError("Got work and output index don't match.");
}
if (visitIndex != 0)
{
this->RaiseError("Got wrong visit value.");
}
}
};
class TestWorkletMapTopoSelect : public TestWorkletMapTopo
{
public:
using MaskType = vtkm::worklet::MaskSelect;
VTKM_EXEC void operator()(const vtkm::Vec<int, 3>& vtkmNotUsed(coords),
const vtkm::Id& vtkmNotUsed(workIndex),
const vtkm::Id& vtkmNotUsed(inputIndex),
const vtkm::Id& vtkmNotUsed(outputIndex),
const vtkm::Id& vtkmNotUsed(visitIndex)) const override
{
// This method should never be called
this->RaiseError("An element was selected, this test selects none.");
}
};
template <typename WorkletType>
struct DoTestWorklet
{
template <typename T>
VTKM_CONT void operator()(T) const
{
vtkm::cont::testing::MakeTestDataSet testDataSet;
vtkm::cont::DataSet dataSet3D = testDataSet.Make3DUniformDataSet0();
vtkm::cont::CellSetStructured<3> cellSet =
dataSet3D.GetCellSet().Cast<vtkm::cont::CellSetStructured<3>>();
vtkm::cont::Invoker invoker;
invoker(WorkletType{}, cellSet, dataSet3D.GetCoordinateSystem());
}
};
template <>
struct DoTestWorklet<TestWorkletMapTopoSelect>
{
template <typename T>
VTKM_CONT void operator()(T) const
{
// 18 are the number of vertices at the DataSet created by Make3DUniformDataSet0
const vtkm::Id selectArraySize = 18;
const vtkm::IdComponent selectArray[selectArraySize] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0 };
auto selectArrayHandle = vtkm::cont::make_ArrayHandle(selectArray, selectArraySize);
vtkm::cont::testing::MakeTestDataSet testDataSet;
vtkm::cont::DataSet dataSet3D = testDataSet.Make3DUniformDataSet0();
vtkm::cont::CellSetStructured<3> cellSet =
dataSet3D.GetCellSet().Cast<vtkm::cont::CellSetStructured<3>>();
vtkm::cont::Invoker invoker;
invoker(TestWorkletMapTopoSelect{},
vtkm::worklet::MaskSelect(selectArrayHandle),
cellSet,
dataSet3D.GetCoordinateSystem());
}
};
void TestWorkletMapField3d(vtkm::cont::DeviceAdapterId id)
{
using HandleTypesToTest3D =
vtkm::List<vtkm::Id, vtkm::Vec2i_32, vtkm::FloatDefault, vtkm::Vec3f_64>;
std::cout << "Testing WorkletMapTopology with ScatterIdentity on device adapter: " << id.GetName()
<< std::endl;
vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet<TestWorkletMapTopoIdentity>(),
HandleTypesToTest3D());
std::cout << "Testing WorkletMapTopology with ScatterUniform on device adapter: " << id.GetName()
<< std::endl;
vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet<TestWorkletMapTopoUniform>(),
HandleTypesToTest3D());
std::cout << "Testing WorkletMapTopology with MaskNone on device adapter: " << id.GetName()
<< std::endl;
vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet<TestWorkletMapTopoNone>(),
HandleTypesToTest3D());
std::cout << "Testing WorkletMapTopology with MaskSelect on device adapter: " << id.GetName()
<< std::endl;
vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet<TestWorkletMapTopoSelect>(),
HandleTypesToTest3D());
}
} // maptopology3d namespace
int UnitTestScatterAndMaskWithTopology(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::RunOnDevice(
maptopology3d::TestWorkletMapField3d, argc, argv);
}