Create ArrayHandleOffsetsToNumComponents

This is a fancy array that takes an array of offsets and converts it to
an array of the number of components for each packed entry.

This replaces the use of `ArrayHandleDecorator` in `CellSetExplicit`.
The two implementation should do the same thing, but the new
`ArrayHandleOffsetsToNumComponents` should be less complex for
compilers.
This commit is contained in:
Kenneth Moreland 2020-09-24 12:16:11 -06:00
parent 3aa2c7521a
commit b9430c52e6
7 changed files with 294 additions and 36 deletions

@ -0,0 +1,29 @@
# Create `ArrayHandleOffsetsToNumComponents`
`ArrayHandleOffsetsToNumComponents` is a fancy array that takes an array of
offsets and converts it to an array of the number of components for each
packed entry.
It is common in VTK-m to pack small vectors of variable sizes into a single
contiguous array. For example, cells in an explicit cell set can each have
a different amount of vertices (triangles = 3, quads = 4, tetra = 4, hexa =
8, etc.). Generally, to access items in this list, you need an array of
components in each entry and the offset for each entry. However, if you
have just the array of offsets in sorted order, you can easily derive the
number of components for each entry by subtracting adjacent entries. This
works best if the offsets array has a size that is one more than the number
of packed vectors with the first entry set to 0 and the last entry set to
the total size of the packed array (the offset to the end).
When packing data of this nature, it is common to start with an array that
is the number of components. You can convert that to an offsets array using
the `vtkm::cont::ConvertNumComponentsToOffsets` function. This will create
an offsets array with one extra entry as previously described. You can then
throw out the original number of components array and use the offsets with
`ArrayHandleOffsetsToNumComponents` to represent both the offsets and num
components while storing only one array.
This replaces the use of `ArrayHandleDecorator` in `CellSetExplicit`.
The two implementation should do the same thing, but the new
`ArrayHandleOffsetsToNumComponents` should be less complex for
compilers.

@ -0,0 +1,186 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_cont_ArrayHandleOffsetsToNumComponents_h
#define vtk_m_cont_ArrayHandleOffsetsToNumComponents_h
#include <vtkm/cont/ArrayHandle.h>
namespace vtkm
{
namespace internal
{
// Note that `ArrayPortalOffsetsToNumComponents` requires a source portal with +1 entry
// to avoid branching. See `ArrayHandleOffsetsToNumComponents` for details.
template <typename OffsetsPortal>
class VTKM_ALWAYS_EXPORT ArrayPortalOffsetsToNumComponents
{
OffsetsPortal Portal;
public:
ArrayPortalOffsetsToNumComponents() = default;
ArrayPortalOffsetsToNumComponents(const OffsetsPortal& portal)
: Portal(portal)
{
}
using ValueType = vtkm::IdComponent;
VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return this->Portal.GetNumberOfValues() - 1; }
VTKM_EXEC_CONT vtkm::IdComponent Get(vtkm::Id index) const
{
return static_cast<vtkm::IdComponent>(this->Portal.Get(index + 1) - this->Portal.Get(index));
}
};
}
} // namespace vtkm::internal
namespace vtkm
{
namespace cont
{
template <typename OffsetsStorageTag>
struct VTKM_ALWAYS_EXPORT StorageTagOffsetsToNumComponents
{
};
namespace internal
{
template <typename OffsetsStorageTag>
class VTKM_ALWAYS_EXPORT
Storage<vtkm::IdComponent, vtkm::cont::StorageTagOffsetsToNumComponents<OffsetsStorageTag>>
{
using OffsetsStorage = vtkm::cont::internal::Storage<vtkm::Id, OffsetsStorageTag>;
public:
using ReadPortalType =
vtkm::internal::ArrayPortalOffsetsToNumComponents<typename OffsetsStorage::ReadPortalType>;
// Writing to ArrayHandleOffsetsToNumComponents not really supported, but we need to define this.
using WritePortalType = ReadPortalType;
VTKM_CONT static constexpr vtkm::IdComponent GetNumberOfBuffers()
{
return OffsetsStorage::GetNumberOfBuffers();
}
VTKM_CONT static vtkm::Id GetNumberOfValues(const vtkm::cont::internal::Buffer* buffers)
{
vtkm::Id numOffsets = OffsetsStorage::GetNumberOfValues(buffers);
if (numOffsets < 1)
{
throw vtkm::cont::ErrorBadValue(
"ArrayHandleOffsetsToNumComponents requires an offsets array with at least one value.");
}
return numOffsets - 1;
}
VTKM_CONT static void ResizeBuffers(vtkm::Id numValues,
vtkm::cont::internal::Buffer* buffers,
vtkm::CopyFlag,
vtkm::cont::Token&)
{
if (numValues == GetNumberOfValues(buffers))
{
// In general, we don't allow resizing of the array, but if it was "allocated" to the
// correct size, we will allow that.
}
else
{
throw vtkm::cont::ErrorBadAllocation(
"Cannot allocate/resize ArrayHandleOffsetsToNumComponents.");
}
}
VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
{
VTKM_ASSERT(OffsetsStorage::GetNumberOfValues(buffers) > 0);
return ReadPortalType(OffsetsStorage::CreateReadPortal(buffers, device, token));
}
VTKM_CONT static WritePortalType CreateWritePortal(const vtkm::cont::internal::Buffer*,
vtkm::cont::DeviceAdapterId,
vtkm::cont::Token&)
{
throw vtkm::cont::ErrorBadAllocation("Cannot write to implicit arrays.");
}
};
} // namespace internal
/// \brief An `ArrayHandle` that converts an array of offsets to an array of `Vec` sizes.
///
/// It is common in VTK-m to pack small vectors of variable sizes into a single contiguous
/// array. For example, cells in an explicit cell set can each have a different amount of
/// vertices (triangles = 3, quads = 4, tetra = 4, hexa = 8, etc.). Generally, to access
/// items in this list, you need an array of components in each entry and the offset for
/// each entry. However, if you have just the array of offsets in sorted order, you can
/// easily derive the number of components for each entry by subtracting adjacent entries.
/// This works best if the offsets array has a size that is one more than the number of
/// packed vectors with the first entry set to 0 and the last entry set to the total size
/// of the packed array (the offset to the end).
///
/// `ArrayHandleOffsetsToNumComponents` decorates an array in exactly this manner. It
/// takes an offsets array and makes it behave like an array of counts. Note that the
/// offsets array must conform to the conditions described above: the offsets are in
/// sorted order and there is one additional entry in the offsets (ending in an offset
/// pointing past the end of the array).
///
/// When packing data of this nature, it is common to start with an array that is the
/// number of components. You can convert that to an offsets array using the
/// `vtkm::cont::ConvertNumComponentsToOffsets` function. This will create an offsets array
/// with one extra entry as previously described. You can then throw out the original
/// number of components array and use the offsets with `ArrayHandleOffsetsToNumComponents`
/// to represent both the offsets and num components while storing only one array.
///
template <class OffsetsArray>
class VTKM_ALWAYS_EXPORT ArrayHandleOffsetsToNumComponents
: public vtkm::cont::ArrayHandle<
vtkm::IdComponent,
vtkm::cont::StorageTagOffsetsToNumComponents<typename OffsetsArray::StorageTag>>
{
VTKM_IS_ARRAY_HANDLE(OffsetsArray);
VTKM_STATIC_ASSERT_MSG((std::is_same<typename OffsetsArray::ValueType, vtkm::Id>::value),
"Offsets array must have a value type of vtkm::Id.");
public:
VTKM_ARRAY_HANDLE_SUBCLASS(
ArrayHandleOffsetsToNumComponents,
(ArrayHandleOffsetsToNumComponents<OffsetsArray>),
(vtkm::cont::ArrayHandle<
vtkm::IdComponent,
vtkm::cont::StorageTagOffsetsToNumComponents<typename OffsetsArray::StorageTag>>));
VTKM_CONT ArrayHandleOffsetsToNumComponents(const OffsetsArray& array)
: Superclass(array.GetBuffers())
{
}
};
template <typename OffsetsStorageTag>
VTKM_CONT vtkm::cont::ArrayHandleOffsetsToNumComponents<
vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorageTag>>
make_ArrayHandleOffsetsToNumComponents(
const vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorageTag>& array)
{
// Converts to correct type.
return array;
}
}
} // namespace vtkm::cont
#endif //vtk_m_cont_ArrayHandleOffsetsToNumComponents_h

@ -30,6 +30,7 @@ set(headers
ArrayHandleImplicit.h
ArrayHandleIndex.h
ArrayHandleMultiplexer.h
ArrayHandleOffsetsToNumComponents.h
ArrayHandlePermutation.h
ArrayHandleRecombineVec.h
ArrayHandleReverse.h

@ -16,7 +16,7 @@
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleDecorator.h>
#include <vtkm/cont/ArrayHandleOffsetsToNumComponents.h>
#include <vtkm/cont/CellSet.h>
#include <vtkm/cont/internal/ConnectivityExplicitInternals.h>
#include <vtkm/exec/ConnectivityExplicit.h>
@ -37,32 +37,6 @@ struct CellSetExplicitConnectivityChooser
using ConnectivityType = vtkm::cont::internal::ConnectivityExplicitInternals<>;
};
// Used with ArrayHandleDecorator to recover the NumIndices array from the
// offsets.
struct NumIndicesDecorator
{
template <typename OffsetsPortal>
struct Functor
{
OffsetsPortal Offsets;
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
vtkm::IdComponent operator()(vtkm::Id cellId) const
{
return static_cast<vtkm::IdComponent>(this->Offsets.Get(cellId + 1) -
this->Offsets.Get(cellId));
}
};
template <typename OffsetsPortal>
static VTKM_CONT Functor<typename std::decay<OffsetsPortal>::type> CreateFunctor(
OffsetsPortal&& portal)
{
return { std::forward<OffsetsPortal>(portal) };
}
};
} // namespace detail
#ifndef VTKM_DEFAULT_SHAPES_STORAGE_TAG
@ -142,8 +116,7 @@ class VTKM_ALWAYS_EXPORT CellSetExplicit : public CellSet
using ConnectivityArrayType = typename ConnectivityType::ConnectivityArrayType;
using OffsetsArrayType = typename ConnectivityType::OffsetsArrayType;
using NumIndicesArrayType =
vtkm::cont::ArrayHandleDecorator<detail::NumIndicesDecorator, OffsetsArrayType>;
using NumIndicesArrayType = vtkm::cont::ArrayHandleOffsetsToNumComponents<OffsetsArrayType>;
using ExecConnectivityType =
vtkm::exec::ConnectivityExplicit<typename ShapesArrayType::ReadPortalType,

@ -14,7 +14,6 @@
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayGetValues.h>
#include <vtkm/cont/ArrayHandleDecorator.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/TryExecute.h>
@ -442,11 +441,8 @@ auto CellSetExplicit<SST, CST, OST>
-> typename ConnectivityChooser<VisitTopology,
IncidentTopology>::NumIndicesArrayType
{
auto offsets = this->GetOffsetsArray(visited, incident);
const vtkm::Id numVals = offsets.GetNumberOfValues() - 1;
return vtkm::cont::make_ArrayHandleDecorator(numVals,
detail::NumIndicesDecorator{},
std::move(offsets));
// Converts to NumIndicesArrayType (which is an ArrayHandleOffsetsToNumComponents)
return this->GetOffsetsArray(visited, incident);
}
//----------------------------------------------------------------------------

@ -43,11 +43,12 @@ set(unit_tests
UnitTestArrayHandleExtractComponent.cxx
UnitTestArrayHandleImplicit.cxx
UnitTestArrayHandleIndex.cxx
UnitTestArrayHandleReverse.cxx
UnitTestArrayHandleOffsetsToNumComponents.cxx
UnitTestArrayHandlePermutation.cxx
UnitTestArrayHandleRandomStandardNormal.cxx
UnitTestArrayHandleRandomUniformBits.cxx
UnitTestArrayHandleRandomUniformReal.cxx
UnitTestArrayHandleReverse.cxx
UnitTestArrayHandleSwizzle.cxx
UnitTestArrayHandleThreadSafety.cxx
UnitTestArrayHandleTransform.cxx

@ -0,0 +1,72 @@
//============================================================================
// 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/ArrayHandleOffsetsToNumComponents.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/CellSetExplicit.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
constexpr vtkm::Id ARRAY_SIZE = 20;
template <typename OffsetsArray, typename ExpectedNumComponents>
void TestOffsetsToNumComponents(const OffsetsArray& offsetsArray,
const ExpectedNumComponents& expectedNumComponents)
{
VTKM_TEST_ASSERT(offsetsArray.GetNumberOfValues() ==
expectedNumComponents.GetNumberOfValues() + 1);
auto numComponents = vtkm::cont::make_ArrayHandleOffsetsToNumComponents(offsetsArray);
VTKM_TEST_ASSERT(numComponents.GetNumberOfValues() == expectedNumComponents.GetNumberOfValues());
VTKM_TEST_ASSERT(
test_equal_portals(numComponents.ReadPortal(), expectedNumComponents.ReadPortal()));
}
void TryNormalOffsets()
{
std::cout << "Normal offset array." << std::endl;
vtkm::cont::ArrayHandle<vtkm::IdComponent> numComponents;
numComponents.Allocate(ARRAY_SIZE);
auto numComponentsPortal = numComponents.WritePortal();
for (vtkm::IdComponent i = 0; i < ARRAY_SIZE; ++i)
{
numComponentsPortal.Set(i, i % 5);
}
auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numComponents);
TestOffsetsToNumComponents(offsets, numComponents);
}
void TryFancyOffsets()
{
std::cout << "Fancy offset array." << std::endl;
vtkm::cont::ArrayHandleCounting<vtkm::Id> offsets(0, 3, ARRAY_SIZE + 1);
TestOffsetsToNumComponents(offsets,
vtkm::cont::ArrayHandleConstant<vtkm::IdComponent>(3, ARRAY_SIZE));
}
void Run()
{
TryNormalOffsets();
TryFancyOffsets();
}
} // anonymous namespace
int UnitTestArrayHandleOffsetsToNumComponents(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(Run, argc, argv);
}