diff --git a/docs/changelog/reverseconnection-precompiled.md b/docs/changelog/reverseconnection-precompiled.md new file mode 100644 index 000000000..064eb9965 --- /dev/null +++ b/docs/changelog/reverseconnection-precompiled.md @@ -0,0 +1,21 @@ +# Compile reverse connectivity builder into vtkm_cont library + +Because `CellSetExplicit` is a templated class, the implementation of +most of its features is part of the header files. One of the things that +was included was the code to build the reverse connectivity links. That +is, it figured out which cells were incident on each point using the +standard connections of which points comprise which cells. + +Of course, building these links is non-trivial, and it used multiple +DPPs to engage the device. It meant that header had to include the +device adapter algorithms and therefore required a device compiler. We +want to minimize this where possible. + +To get around this issue, a non-templated function was added to find the +reverse connections of a `CellSetExplicit`. It does this by passing in +`UnknownArrayHandle`s for the input arrays. (The output visit-points- +with-cells arrays are standard across all template instances.) The +implementation first iterates over all `CellSetExplicit` versions in +`VTKM_DEFAULT_CELL_SETS` and attempts to retrieve arrays of those types. +In the unlikely event that none of these arrays work, it copies the data +to `ArrayHandle` and uses those. diff --git a/vtkm/cont/CellSetExplicit.cxx b/vtkm/cont/CellSetExplicit.cxx index 9f0c5a3ab..3f76f4db2 100644 --- a/vtkm/cont/CellSetExplicit.cxx +++ b/vtkm/cont/CellSetExplicit.cxx @@ -11,6 +11,91 @@ #include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +template +void DoBuildReverseConnectivity( + const vtkm::cont::ArrayHandle& connections, + const vtkm::cont::ArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId suggestedDevice) +{ + using CellsWithPointsConnectivity = vtkm::cont::internal:: + ConnectivityExplicitInternals; + + // Make a fake visitCellsWithPoints to pass to ComputeRConnTable. This is a bit of a + // patchwork from changing implementation. + CellsWithPointsConnectivity visitCellsWithPoints; + visitCellsWithPoints.ElementsValid = true; + visitCellsWithPoints.Connectivity = connections; + visitCellsWithPoints.Offsets = offsets; + + bool success = + vtkm::cont::TryExecuteOnDevice(suggestedDevice, [&](vtkm::cont::DeviceAdapterId realDevice) { + vtkm::cont::internal::ComputeRConnTable( + visitPointsWithCells, visitCellsWithPoints, numberOfPoints, realDevice); + return true; + }); + + if (!success) + { + throw vtkm::cont::ErrorExecution("Failed to run CellSetExplicit reverse " + "connectivity builder."); + } +} + +struct BuildReverseConnectivityForCellSetType +{ + template + void operator()( + const vtkm::cont::CellSetExplicit&, + const vtkm::cont::UnknownArrayHandle& connections, + const vtkm::cont::UnknownArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId device) + { + if (visitPointsWithCells.ElementsValid) + { + return; // Already computed reverse + } + + using ConnectArrayType = vtkm::cont::ArrayHandle; + using OffsetArrayType = vtkm::cont::ArrayHandle; + if (connections.CanConvert() && offsets.CanConvert()) + { + DoBuildReverseConnectivity(connections.AsArrayHandle(), + offsets.AsArrayHandle(), + numberOfPoints, + visitPointsWithCells, + device); + } + } + + template + void operator()(const CellSetType&, + const vtkm::cont::UnknownArrayHandle&, + const vtkm::cont::UnknownArrayHandle&, + vtkm::Id, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit&, + vtkm::cont::DeviceAdapterId) + { + // Not an explicit cell set, so skip. + } +}; + +} // anonymous namespace + namespace vtkm { namespace cont @@ -21,5 +106,45 @@ template class VTKM_CONT_EXPORT CellSetExplicit::StorageTag, VTKM_DEFAULT_CONNECTIVITY_STORAGE_TAG, typename vtkm::cont::ArrayHandleCounting::StorageTag>; + +namespace detail +{ + +void BuildReverseConnectivity( + const vtkm::cont::UnknownArrayHandle& connections, + const vtkm::cont::UnknownArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId device) +{ + if (visitPointsWithCells.ElementsValid) + { + return; // Already computed + } + + vtkm::ListForEach(BuildReverseConnectivityForCellSetType{}, + VTKM_DEFAULT_CELL_SET_LIST{}, + connections, + offsets, + numberOfPoints, + visitPointsWithCells, + device); + + if (!visitPointsWithCells.ElementsValid) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "BuildReverseConnectivity failed for all known cell set types. " + "Attempting to copy connectivity arrays."); + vtkm::cont::ArrayHandle connectionsCopy; + vtkm::cont::ArrayCopy(connections, connectionsCopy); + vtkm::cont::ArrayHandle offsetsCopy; + vtkm::cont::ArrayCopy(offsets, offsetsCopy); + DoBuildReverseConnectivity( + connectionsCopy, offsetsCopy, numberOfPoints, visitPointsWithCells, device); + } } -} + +} // namespace vtkm::cont::detail + +} // namespace vtkm::cont +} // namespace vtkm diff --git a/vtkm/cont/CellSetExplicit.h b/vtkm/cont/CellSetExplicit.h index 166dbeee7..3a290e44b 100644 --- a/vtkm/cont/CellSetExplicit.h +++ b/vtkm/cont/CellSetExplicit.h @@ -11,6 +11,7 @@ #define vtk_m_cont_CellSetExplicit_h #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include @@ -37,6 +39,20 @@ struct CellSetExplicitConnectivityChooser using ConnectivityType = vtkm::cont::internal::ConnectivityExplicitInternals<>; }; +// The connectivity generally used for the visit-points-with-cells connectivity. +// This type of connectivity does not have variable shape types, and since it is +// never really provided externally we can use the defaults for the other arrays. +using DefaultVisitPointsWithCellsConnectivityExplicit = + vtkm::cont::internal::ConnectivityExplicitInternals< + typename ArrayHandleConstant::StorageTag>; + +VTKM_CONT_EXPORT void BuildReverseConnectivity( + const vtkm::cont::UnknownArrayHandle& connections, + const vtkm::cont::UnknownArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId device); + } // namespace detail #ifndef VTKM_DEFAULT_SHAPES_STORAGE_TAG @@ -229,11 +245,22 @@ public: protected: VTKM_CONT void BuildConnectivity(vtkm::cont::DeviceAdapterId, vtkm::TopologyElementTagCell, - vtkm::TopologyElementTagPoint) const; + vtkm::TopologyElementTagPoint) const + { + VTKM_ASSERT(this->Data->CellPointIds.ElementsValid); + // no-op + } - VTKM_CONT void BuildConnectivity(vtkm::cont::DeviceAdapterId, + VTKM_CONT void BuildConnectivity(vtkm::cont::DeviceAdapterId device, vtkm::TopologyElementTagPoint, - vtkm::TopologyElementTagCell) const; + vtkm::TopologyElementTagCell) const + { + detail::BuildReverseConnectivity(this->Data->CellPointIds.Connectivity, + this->Data->CellPointIds.Offsets, + this->Data->NumberOfPoints, + this->Data->PointCellIds, + device); + } VTKM_CONT bool HasConnectivityImpl(vtkm::TopologyElementTagCell, vtkm::TopologyElementTagPoint) const @@ -336,8 +363,7 @@ struct CellSetExplicitConnectivityChooser::StorageTag>; + using ConnectivityType = vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit; }; } // namespace detail diff --git a/vtkm/cont/CellSetExplicit.hxx b/vtkm/cont/CellSetExplicit.hxx index 8a7e706ef..e9c5e76cc 100644 --- a/vtkm/cont/CellSetExplicit.hxx +++ b/vtkm/cont/CellSetExplicit.hxx @@ -9,14 +9,10 @@ //============================================================================ #ifndef vtk_m_cont_CellSetExplicit_hxx #define vtk_m_cont_CellSetExplicit_hxx -#include #include -#include #include #include -#include -#include // This file uses a lot of very verbose identifiers and the clang formatted // code quickly becomes unreadable. Stick with manual formatting for now. @@ -478,74 +474,6 @@ void CellSetExplicit::DeepCopy(const CellSet* src) this->Fill(other->GetNumberOfPoints(), shapes, conn, offsets); } -//---------------------------------------------------------------------------- - -namespace detail -{ - -template -struct BuildPointCellIdsFunctor -{ - BuildPointCellIdsFunctor(CellPointIdsT &cellPointIds, - PointCellIdsT &pointCellIds, - vtkm::Id numberOfPoints) - : CellPointIds(cellPointIds) - , PointCellIds(pointCellIds) - , NumberOfPoints(numberOfPoints) - { - } - - template - bool operator()(Device) const - { - internal::ComputeRConnTable(this->PointCellIds, - this->CellPointIds, - this->NumberOfPoints, - Device{}); - return true; - } - - CellPointIdsT &CellPointIds; - PointCellIdsT &PointCellIds; - vtkm::Id NumberOfPoints; -}; - -} // detail - -template -VTKM_CONT -void CellSetExplicit -::BuildConnectivity(vtkm::cont::DeviceAdapterId, - vtkm::TopologyElementTagCell, - vtkm::TopologyElementTagPoint) const -{ - VTKM_ASSERT(this->Data->CellPointIds.ElementsValid); - // no-op -} - -template -VTKM_CONT -void CellSetExplicit -::BuildConnectivity(vtkm::cont::DeviceAdapterId device, - vtkm::TopologyElementTagPoint, - vtkm::TopologyElementTagCell) const -{ - if (!this->Data->PointCellIds.ElementsValid) - { - auto self = const_cast(this); - using Func = detail::BuildPointCellIdsFunctor; - - auto functor = Func(self->Data->CellPointIds, - self->Data->PointCellIds, - self->Data->NumberOfPoints); - - if (!vtkm::cont::TryExecuteOnDevice(device, functor)) - { - throw vtkm::cont::ErrorExecution("Failed to run CellSetExplicit reverse " - "connectivity builder."); - } - } -} } } // vtkm::cont diff --git a/vtkm/cont/CellSetPermutation.h b/vtkm/cont/CellSetPermutation.h index 66f06f9e5..a22165fb6 100644 --- a/vtkm/cont/CellSetPermutation.h +++ b/vtkm/cont/CellSetPermutation.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include diff --git a/vtkm/cont/internal/ConnectivityExplicitInternals.h b/vtkm/cont/internal/ConnectivityExplicitInternals.h index 098890b44..f94cbb393 100644 --- a/vtkm/cont/internal/ConnectivityExplicitInternals.h +++ b/vtkm/cont/internal/ConnectivityExplicitInternals.h @@ -11,14 +11,10 @@ #define vtk_m_cont_internal_ConnectivityExplicitInternals_h #include -#include -#include #include #include #include #include -#include -#include namespace vtkm { @@ -82,151 +78,6 @@ struct ConnectivityExplicitInternals } } }; - -// Pass through (needed for ReverseConnectivityBuilder) -struct PassThrough -{ - VTKM_EXEC vtkm::Id operator()(const vtkm::Id& val) const { return val; } -}; - -// Compute cell id from input connectivity: -// Find the upper bound of the conn idx in the offsets table and subtract 1 -// -// Example: -// Offsets: | 0 | 3 | 6 | 10 | -// Conn: | 0 1 2 | 0 1 3 | 2 4 5 6 | 1 3 5 | -// ConnIdx: | 0 1 2 | 3 4 5 | 6 7 8 9 | 10 11 12 | -// UpprBnd: | 1 1 1 | 2 2 2 | 3 3 3 3 | 4 4 4 | -// CellIdx: | 0 0 0 | 1 1 1 | 2 2 2 2 | 3 3 3 | -template -struct ConnIdxToCellIdCalc -{ - OffsetsPortalType Offsets; - - VTKM_CONT - ConnIdxToCellIdCalc(const OffsetsPortalType& offsets) - : Offsets(offsets) - { - } - - VTKM_EXEC - vtkm::Id operator()(vtkm::Id inIdx) const - { - // Compute the upper bound index: - vtkm::Id upperBoundIdx; - { - vtkm::Id first = 0; - vtkm::Id length = this->Offsets.GetNumberOfValues(); - - while (length > 0) - { - vtkm::Id halfway = length / 2; - vtkm::Id pos = first + halfway; - vtkm::Id val = this->Offsets.Get(pos); - if (val <= inIdx) - { - first = pos + 1; - length -= halfway + 1; - } - else - { - length = halfway; - } - } - - upperBoundIdx = first; - } - - return upperBoundIdx - 1; - } -}; - -// Much easier for CellSetSingleType: -struct ConnIdxToCellIdCalcSingleType -{ - vtkm::IdComponent CellSize; - - VTKM_CONT - ConnIdxToCellIdCalcSingleType(vtkm::IdComponent cellSize) - : CellSize(cellSize) - { - } - - VTKM_EXEC - vtkm::Id operator()(vtkm::Id inIdx) const { return inIdx / this->CellSize; } -}; - -template -void ComputeRConnTable(RConnTableT& rConnTable, - const ConnTableT& connTable, - vtkm::Id numberOfPoints, - vtkm::cont::DeviceAdapterId device) -{ - if (rConnTable.ElementsValid) - { - return; - } - - const auto& conn = connTable.Connectivity; - auto& rConn = rConnTable.Connectivity; - auto& rOffsets = rConnTable.Offsets; - const vtkm::Id rConnSize = conn.GetNumberOfValues(); - - { - vtkm::cont::Token token; - const auto offInPortal = connTable.Offsets.PrepareForInput(device, token); - - PassThrough idxCalc{}; - ConnIdxToCellIdCalc cellIdCalc{ offInPortal }; - - vtkm::cont::internal::ReverseConnectivityBuilder builder; - builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); - } - - rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( - static_cast(CELL_SHAPE_VERTEX), numberOfPoints); - rConnTable.ElementsValid = true; -} - -// Specialize for CellSetSingleType: -template -void ComputeRConnTable(RConnTableT& rConnTable, - const ConnectivityExplicitInternals< // SingleType specialization types: - typename vtkm::cont::ArrayHandleConstant::StorageTag, - ConnectivityStorageTag, - typename vtkm::cont::ArrayHandleCounting::StorageTag>& connTable, - vtkm::Id numberOfPoints, - vtkm::cont::DeviceAdapterId device) -{ - if (rConnTable.ElementsValid) - { - return; - } - - const auto& conn = connTable.Connectivity; - auto& rConn = rConnTable.Connectivity; - auto& rOffsets = rConnTable.Offsets; - const vtkm::Id rConnSize = conn.GetNumberOfValues(); - - const vtkm::IdComponent cellSize = [&]() -> vtkm::IdComponent { - if (connTable.Offsets.GetNumberOfValues() >= 2) - { - const auto firstTwo = vtkm::cont::ArrayGetValues({ 0, 1 }, connTable.Offsets); - return static_cast(firstTwo[1] - firstTwo[0]); - } - return 0; - }(); - - PassThrough idxCalc{}; - ConnIdxToCellIdCalcSingleType cellIdCalc{ cellSize }; - - vtkm::cont::internal::ReverseConnectivityBuilder builder; - builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); - - rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( - static_cast(CELL_SHAPE_VERTEX), numberOfPoints); - rConnTable.ElementsValid = true; -} } } } // namespace vtkm::cont::internal diff --git a/vtkm/cont/internal/ReverseConnectivityBuilder.h b/vtkm/cont/internal/ReverseConnectivityBuilder.h index e18695ed0..b0a520a0b 100644 --- a/vtkm/cont/internal/ReverseConnectivityBuilder.h +++ b/vtkm/cont/internal/ReverseConnectivityBuilder.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -211,6 +212,152 @@ public: } } }; + +// Pass through (needed for ReverseConnectivityBuilder) +struct PassThrough +{ + VTKM_EXEC vtkm::Id operator()(const vtkm::Id& val) const { return val; } +}; + +// Compute cell id from input connectivity: +// Find the upper bound of the conn idx in the offsets table and subtract 1 +// +// Example: +// Offsets: | 0 | 3 | 6 | 10 | +// Conn: | 0 1 2 | 0 1 3 | 2 4 5 6 | 1 3 5 | +// ConnIdx: | 0 1 2 | 3 4 5 | 6 7 8 9 | 10 11 12 | +// UpprBnd: | 1 1 1 | 2 2 2 | 3 3 3 3 | 4 4 4 | +// CellIdx: | 0 0 0 | 1 1 1 | 2 2 2 2 | 3 3 3 | +template +struct ConnIdxToCellIdCalc +{ + OffsetsPortalType Offsets; + + VTKM_CONT + ConnIdxToCellIdCalc(const OffsetsPortalType& offsets) + : Offsets(offsets) + { + } + + VTKM_EXEC + vtkm::Id operator()(vtkm::Id inIdx) const + { + // Compute the upper bound index: + vtkm::Id upperBoundIdx; + { + vtkm::Id first = 0; + vtkm::Id length = this->Offsets.GetNumberOfValues(); + + while (length > 0) + { + vtkm::Id halfway = length / 2; + vtkm::Id pos = first + halfway; + vtkm::Id val = this->Offsets.Get(pos); + if (val <= inIdx) + { + first = pos + 1; + length -= halfway + 1; + } + else + { + length = halfway; + } + } + + upperBoundIdx = first; + } + + return upperBoundIdx - 1; + } +}; + +// Much easier for CellSetSingleType: +struct ConnIdxToCellIdCalcSingleType +{ + vtkm::IdComponent CellSize; + + VTKM_CONT + ConnIdxToCellIdCalcSingleType(vtkm::IdComponent cellSize) + : CellSize(cellSize) + { + } + + VTKM_EXEC + vtkm::Id operator()(vtkm::Id inIdx) const { return inIdx / this->CellSize; } +}; + +template +void ComputeRConnTable(RConnTableT& rConnTable, + const ConnTableT& connTable, + vtkm::Id numberOfPoints, + vtkm::cont::DeviceAdapterId device) +{ + if (rConnTable.ElementsValid) + { + return; + } + + const auto& conn = connTable.Connectivity; + auto& rConn = rConnTable.Connectivity; + auto& rOffsets = rConnTable.Offsets; + const vtkm::Id rConnSize = conn.GetNumberOfValues(); + + { + vtkm::cont::Token token; + const auto offInPortal = connTable.Offsets.PrepareForInput(device, token); + + PassThrough idxCalc{}; + ConnIdxToCellIdCalc cellIdCalc{ offInPortal }; + + vtkm::cont::internal::ReverseConnectivityBuilder builder; + builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); + } + + rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( + static_cast(CELL_SHAPE_VERTEX), numberOfPoints); + rConnTable.ElementsValid = true; +} + +// Specialize for CellSetSingleType: +template +void ComputeRConnTable(RConnTableT& rConnTable, + const ConnectivityExplicitInternals< // SingleType specialization types: + typename vtkm::cont::ArrayHandleConstant::StorageTag, + ConnectivityStorageTag, + typename vtkm::cont::ArrayHandleCounting::StorageTag>& connTable, + vtkm::Id numberOfPoints, + vtkm::cont::DeviceAdapterId device) +{ + if (rConnTable.ElementsValid) + { + return; + } + + const auto& conn = connTable.Connectivity; + auto& rConn = rConnTable.Connectivity; + auto& rOffsets = rConnTable.Offsets; + const vtkm::Id rConnSize = conn.GetNumberOfValues(); + + const vtkm::IdComponent cellSize = [&]() -> vtkm::IdComponent { + if (connTable.Offsets.GetNumberOfValues() >= 2) + { + const auto firstTwo = vtkm::cont::ArrayGetValues({ 0, 1 }, connTable.Offsets); + return static_cast(firstTwo[1] - firstTwo[0]); + } + return 0; + }(); + + PassThrough idxCalc{}; + ConnIdxToCellIdCalcSingleType cellIdCalc{ cellSize }; + + vtkm::cont::internal::ReverseConnectivityBuilder builder; + builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); + + rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( + static_cast(CELL_SHAPE_VERTEX), numberOfPoints); + rConnTable.ElementsValid = true; +} + } } } // end namespace vtkm::cont::internal