diff --git a/vtkm/exec/arg/ThreadIndicesExtrude.h b/vtkm/exec/arg/ThreadIndicesExtrude.h index 98bb55a94..367e9fc10 100644 --- a/vtkm/exec/arg/ThreadIndicesExtrude.h +++ b/vtkm/exec/arg/ThreadIndicesExtrude.h @@ -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; }; diff --git a/vtkm/exec/arg/ThreadIndicesPointNeighborhood.h b/vtkm/exec/arg/ThreadIndicesPointNeighborhood.h index d227d1fe1..fec183e2b 100644 --- a/vtkm/exec/arg/ThreadIndicesPointNeighborhood.h +++ b/vtkm/exec/arg/ThreadIndicesPointNeighborhood.h @@ -72,6 +72,26 @@ public: { } + template + VTKM_EXEC ThreadIndicesPointNeighborhood( + const vtkm::Id3& threadIndex3D, + vtkm::Id threadIndex1D, + vtkm::Id inputIndex, + vtkm::IdComponent visitIndex, + vtkm::Id outputIndex, + const vtkm::exec::ConnectivityStructured& connectivity, + vtkm::Id globalThreadIndexOffset = 0) + : State(threadIndex3D, detail::To3D(connectivity.GetPointDimensions())) + , ThreadIndex(threadIndex1D) + , InputIndex(inputIndex) + , OutputIndex(outputIndex) + , VisitIndex(visitIndex) + , GlobalThreadIndexOffset(globalThreadIndexOffset) + { + } + template VTKM_EXEC ThreadIndicesPointNeighborhood( vtkm::Id threadIndex, diff --git a/vtkm/exec/arg/ThreadIndicesTopologyMap.h b/vtkm/exec/arg/ThreadIndicesTopologyMap.h index 8d3a95c93..8c59a7b8e 100644 --- a/vtkm/exec/arg/ThreadIndicesTopologyMap.h +++ b/vtkm/exec/arg/ThreadIndicesTopologyMap.h @@ -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 diff --git a/vtkm/worklet/WorkletMapTopology.h b/vtkm/worklet/WorkletMapTopology.h index 4933b1328..d989160f9 100644 --- a/vtkm/worklet/WorkletMapTopology.h +++ b/vtkm/worklet/WorkletMapTopology.h @@ -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::value; + static constexpr bool IsMaskNone = std::is_same::value; + + template + using EnableFnWhen = typename std::enable_if::type; + +public: + /// Optimized for ScatterIdentity and MaskNone VTKM_SUPPRESS_EXEC_WARNINGS template - VTKM_EXEC vtkm::exec::arg::ThreadIndicesTopologyMap 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> + 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; - VTKM_STATIC_ASSERT_MSG(ScatterCheck::value, - "Scheduling on 3D topologies only works with default ScatterIdentity."); - using MaskCheck = std::is_same; - VTKM_STATIC_ASSERT_MSG(MaskCheck::value, - "Scheduling on 3D topologies only works with default MaskNone."); - return vtkm::exec::arg::ThreadIndicesTopologyMap( threadIndex3D, threadIndex1D, connectivity, globalThreadIndexOffset); } + + /// Default version + VTKM_SUPPRESS_EXEC_WARNINGS + template + VTKM_EXEC EnableFnWhen> + 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(threadIndex3D, + threadIndex1D, + outToIn.Get(outIndex), + visit.Get(outIndex), + outIndex, + connectivity, + globalThreadIndexOffset); + } }; /// Base class for worklets that map from Points to Cells. diff --git a/vtkm/worklet/WorkletPointNeighborhood.h b/vtkm/worklet/WorkletPointNeighborhood.h index cdf3d5138..0b4b274e4 100644 --- a/vtkm/worklet/WorkletPointNeighborhood.h +++ b/vtkm/worklet/WorkletPointNeighborhood.h @@ -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::value; + static constexpr bool IsMaskNone = std::is_same::value; + +public: + template + using EnableFnWhen = typename std::enable_if::type; + VTKM_SUPPRESS_EXEC_WARNINGS template - VTKM_EXEC vtkm::exec::arg::ThreadIndicesPointNeighborhood GetThreadIndices( + typename InputDomainType, + bool S = IsScatterIdentity, + bool M = IsMaskNone> + VTKM_EXEC EnableFnWhen 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; - VTKM_STATIC_ASSERT_MSG(ScatterCheck::value, - "Scheduling on 3D topologies only works with default ScatterIdentity."); - using MaskCheck = std::is_same; - 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 + VTKM_EXEC EnableFnWhen + 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); + } }; } } diff --git a/vtkm/worklet/testing/CMakeLists.txt b/vtkm/worklet/testing/CMakeLists.txt index 55d40c239..0a26ec6da 100644 --- a/vtkm/worklet/testing/CMakeLists.txt +++ b/vtkm/worklet/testing/CMakeLists.txt @@ -58,6 +58,7 @@ set(unit_tests UnitTestScatterPermutation.cxx UnitTestSplatKernels.cxx UnitTestSplitSharpEdges.cxx + UnitTestScatterAndMaskWithTopology.cxx UnitTestStreamingSine.cxx UnitTestStreamLineUniformGrid.cxx UnitTestStreamSurface.cxx diff --git a/vtkm/worklet/testing/UnitTestScatterAndMaskWithTopology.cxx b/vtkm/worklet/testing/UnitTestScatterAndMaskWithTopology.cxx new file mode 100644 index 000000000..dd49fa072 --- /dev/null +++ b/vtkm/worklet/testing/UnitTestScatterAndMaskWithTopology.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 + +#include +#include +#include +#include + +#include +#include +#include + +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& 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& 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& 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& 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& 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 +struct DoTestWorklet +{ + template + 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::Invoker invoker; + invoker(WorkletType{}, cellSet, dataSet3D.GetCoordinateSystem()); + } +}; + +template <> +struct DoTestWorklet +{ + template + 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::Invoker invoker; + invoker(TestWorkletMapTopoSelect{}, + vtkm::worklet::MaskSelect(selectArrayHandle), + cellSet, + dataSet3D.GetCoordinateSystem()); + } +}; + +void TestWorkletMapField3d(vtkm::cont::DeviceAdapterId id) +{ + using HandleTypesToTest3D = + vtkm::List; + + std::cout << "Testing WorkletMapTopology with ScatterIdentity on device adapter: " << id.GetName() + << std::endl; + + vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet(), + HandleTypesToTest3D()); + + std::cout << "Testing WorkletMapTopology with ScatterUniform on device adapter: " << id.GetName() + << std::endl; + + vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet(), + HandleTypesToTest3D()); + + std::cout << "Testing WorkletMapTopology with MaskNone on device adapter: " << id.GetName() + << std::endl; + + vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet(), + HandleTypesToTest3D()); + + std::cout << "Testing WorkletMapTopology with MaskSelect on device adapter: " << id.GetName() + << std::endl; + + vtkm::testing::Testing::TryTypes(maptopology3d::DoTestWorklet(), + HandleTypesToTest3D()); +} + +} // maptopology3d namespace + +int UnitTestScatterAndMaskWithTopology(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::RunOnDevice( + maptopology3d::TestWorkletMapField3d, argc, argv); +}