Add ArrayExtractComponent function

`ArrayExtractComponent` allows you to get a component of an array.
Unlike `ArrayHandleExtractComponent`, the type you get is always the
same: an `ArrayHandleStride`. This way, you can get an array that
contains the data of an extracted component with less templating and
potentially dramatically reduce the amount of code generated (although
some runtime integer arithmetic is added).
This commit is contained in:
Kenneth Moreland 2020-09-10 17:54:12 -06:00
parent 0ab3edd87d
commit 760553725c
15 changed files with 947 additions and 15 deletions

@ -0,0 +1,189 @@
//============================================================================
// 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_ArrayExtractComponent_h
#define vtk_m_cont_ArrayExtractComponent_h
#include <vtkm/cont/ArrayHandleBasic.h>
#include <vtkm/cont/ArrayHandleStride.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/TypeTraits.h>
#include <vtkm/VecFlat.h>
#include <vtkm/VecTraits.h>
#include <vtkm/cont/vtkm_cont_export.h>
namespace vtkm
{
namespace cont
{
namespace internal
{
// Note: Using partial template specialization instead of function overloading to
// specialize ArrayExtractComponent for different types of array handles. This is
// because function overloading from a templated function is done when the template
// is defined rather than where it is resolved. This causes problems when extracting
// components of, say, an ArrayHandleMultiplexer holding an ArrayHandleSOA.
template <typename T, typename S>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>
ArrayExtractComponentFallback(const vtkm::cont::ArrayHandle<T, S>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy)
{
if (allowCopy != vtkm::CopyFlag::On)
{
throw vtkm::cont::ErrorBadValue("Cannot extract component of " +
vtkm::cont::TypeToString<vtkm::cont::ArrayHandle<T, S>>() +
" without copying");
}
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
"Extracting component " << componentIndex << " of "
<< vtkm::cont::TypeToString<vtkm::cont::ArrayHandle<T, S>>()
<< " requires an inefficient memory copy.");
using BaseComponentType = typename vtkm::VecTraits<T>::BaseComponentType;
vtkm::Id numValues = src.GetNumberOfValues();
vtkm::cont::ArrayHandleStride<BaseComponentType> dest;
dest.Allocate(numValues);
auto srcPortal = src.ReadPortal();
auto destPortal = dest.WritePortal();
for (vtkm::Id arrayIndex = 0; arrayIndex < numValues; ++arrayIndex)
{
destPortal.Set(arrayIndex,
vtkm::internal::GetFlatVecComponent(srcPortal.Get(arrayIndex), componentIndex));
}
return dest;
}
template <typename S>
struct ArrayExtractComponentImpl
{
template <typename T>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> operator()(
const vtkm::cont::ArrayHandle<T, S>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
// This is the slow "default" implementation. ArrayHandle implementations should provide
// more efficient overloads where applicable.
return vtkm::cont::internal::ArrayExtractComponentFallback(src, componentIndex, allowCopy);
}
};
template <>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagStride>
{
template <typename T>
vtkm::cont::ArrayHandleStride<T> operator()(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagStride>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag vtkmNotUsed(allowCopy)) const
{
VTKM_ASSERT(componentIndex == 0);
return src;
}
template <typename T, vtkm::IdComponent N>
auto operator()(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, vtkm::cont::StorageTagStride>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
-> decltype((*this)(vtkm::cont::ArrayHandleStride<T>{}, componentIndex, allowCopy))
{
constexpr vtkm::IdComponent subStride = vtkm::internal::TotalNumComponents<T>::value;
vtkm::cont::ArrayHandleStride<vtkm::Vec<T, N>> array(src);
vtkm::cont::ArrayHandleStride<T> tmpIn(array.GetBuffers()[1],
array.GetNumberOfValues(),
array.GetStride() * N,
(array.GetOffset() * N) + (componentIndex / subStride),
array.GetModulo() * N,
array.GetDivisor());
return (*this)(tmpIn, componentIndex % subStride, allowCopy);
}
};
template <>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagBasic>
{
template <typename T>
auto operator()(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
-> decltype(
ArrayExtractComponentImpl<vtkm::cont::StorageTagStride>{}(vtkm::cont::ArrayHandleStride<T>{},
componentIndex,
allowCopy))
{
return ArrayExtractComponentImpl<vtkm::cont::StorageTagStride>{}(
vtkm::cont::ArrayHandleStride<T>(src, src.GetNumberOfValues(), 1, 0),
componentIndex,
allowCopy);
}
};
} // namespace internal
/// \brief Pulls a component out of an `ArrayHandle`.
///
/// Given an `ArrayHandle` of any type, `ArrayExtractComponent` returns an
/// `ArrayHandleStride` of the base component type that contains the data for the
/// specified array component. This function can be used to apply an operation on
/// an `ArrayHandle` one component at a time. Because the array type is always
/// `ArrayHandleStride`, you can drastically cut down on the number of templates
/// to instantiate (at a possible cost to performance).
///
/// Note that `ArrayExtractComponent` will flatten out the indices of any vec value
/// type and return an `ArrayExtractComponent` of the base component type. For
/// example, if you call `ArrayExtractComponent` on an `ArrayHandle` with a value
/// type of `vtkm::Vec<vtkm::Vec<vtkm::Float32, 2>, 3>`, you will get an
/// `ArrayExtractComponent<vtkm::Float32>` returned. The `componentIndex` provided
/// will be applied to the nested vector in depth first order. So in the previous
/// example, a `componentIndex` of 0 gets the values at [0][0], `componentIndex`
/// of 1 gets [0][1], `componentIndex` of 2 gets [1][0], and so on.
///
/// Some `ArrayHandle`s allow this method to return an `ArrayHandleStride` that
/// shares the same memory as the the original `ArrayHandle`. This form will be
/// used if possible. In this case, if data are written into the `ArrayHandleStride`,
/// they are also written into the original `ArrayHandle`. However, other forms will
/// require copies into a new array. In this case, writes into `ArrayHandleStride`
/// will not affect the original `ArrayHandle`.
///
/// For some operations, such as writing into an output array, this behavior of
/// shared arrays is necessary. For this case, the optional argument `allowCopy`
/// can be set to `vtkm::CopyFlag::Off` to prevent the copying behavior into the
/// return `ArrayHandleStride`. If this is the case, an `ErrorBadValue` is thrown.
/// If the arrays can be shared, they always will be regardless of the value of
/// `allowCopy`.
///
/// Many forms of `ArrayHandle` have optimized versions to pull out a component.
/// Some, however, do not. In these cases, a fallback array copy, done in serial,
/// will be performed. A warning will be logged to alert users of this likely
/// performance bottleneck.
///
/// As an implementation note, this function should not be overloaded directly.
/// Instead, `ArrayHandle` implementations should provide a specialization of
/// `vtkm::cont::internal::ArrayExtractComponentImpl`.
///
template <typename T, typename S>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> ArrayExtractComponent(
const vtkm::cont::ArrayHandle<T, S>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy = vtkm::CopyFlag::On)
{
return internal::ArrayExtractComponentImpl<S>{}(src, componentIndex, allowCopy);
}
}
} // namespace vtkm::cont
#endif //vtk_m_cont_ArrayExtractComponent_h

@ -12,6 +12,7 @@
#include <vtkm/Assert.h>
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ErrorBadAllocation.h>
#include <vtkm/cont/Token.h>
@ -358,6 +359,103 @@ VTKM_CONT
return ArrayHandleCartesianProduct<FirstHandleType, SecondHandleType, ThirdHandleType>(
first, second, third);
}
//--------------------------------------------------------------------------------
// Specialization of ArrayExtractComponent
namespace internal
{
template <typename... STs>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagCartesianProduct<STs...>>
{
template <typename T>
vtkm::cont::ArrayHandleStride<T> AdjustStrideForComponent(
const vtkm::cont::ArrayHandleStride<T>& componentArray,
const vtkm::Id3& dims,
vtkm::IdComponent component,
vtkm::Id totalNumValues) const
{
VTKM_ASSERT(componentArray.GetModulo() == 0);
VTKM_ASSERT(componentArray.GetDivisor() == 1);
vtkm::Id modulo = 0;
if (component < 2)
{
modulo = dims[component];
}
vtkm::Id divisor = 1;
for (vtkm::IdComponent c = 0; c < component; ++c)
{
divisor *= dims[c];
}
return vtkm::cont::ArrayHandleStride<T>(componentArray.GetBasicArray(),
totalNumValues,
componentArray.GetStride(),
componentArray.GetOffset(),
modulo,
divisor);
}
template <typename T, typename ST, typename CartesianArrayType>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>
GetStrideForComponentArray(const vtkm::cont::ArrayHandle<T, ST>& componentArray,
const CartesianArrayType& cartesianArray,
vtkm::IdComponent subIndex,
vtkm::IdComponent productIndex,
vtkm::CopyFlag allowCopy) const
{
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> strideArray =
ArrayExtractComponentImpl<ST>{}(componentArray, subIndex, allowCopy);
if ((strideArray.GetModulo() != 0) || (strideArray.GetDivisor() != 1))
{
// If the sub array has its own modulo and/or divisor, that will likely interfere
// with this math. Give up and fall back to simple copy.
constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat<T>::NUM_COMPONENTS;
return vtkm::cont::internal::ArrayExtractComponentFallback(
cartesianArray, (productIndex * NUM_SUB_COMPONENTS) + subIndex, allowCopy);
}
vtkm::Id3 dims = { cartesianArray.GetFirstArray().GetNumberOfValues(),
cartesianArray.GetSecondArray().GetNumberOfValues(),
cartesianArray.GetThirdArray().GetNumberOfValues() };
return this->AdjustStrideForComponent(
strideArray, dims, productIndex, cartesianArray.GetNumberOfValues());
}
template <typename T>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> operator()(
const vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>, vtkm::cont::StorageTagCartesianProduct<STs...>>&
src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
vtkm::cont::ArrayHandleCartesianProduct<vtkm::cont::ArrayHandle<T, STs>...> array(src);
constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat<T>::NUM_COMPONENTS;
vtkm::IdComponent subIndex = componentIndex % NUM_SUB_COMPONENTS;
vtkm::IdComponent productIndex = componentIndex / NUM_SUB_COMPONENTS;
switch (productIndex)
{
case 0:
return this->GetStrideForComponentArray(
array.GetFirstArray(), array, subIndex, productIndex, allowCopy);
case 1:
return this->GetStrideForComponentArray(
array.GetSecondArray(), array, subIndex, productIndex, allowCopy);
case 2:
return this->GetStrideForComponentArray(
array.GetThirdArray(), array, subIndex, productIndex, allowCopy);
default:
throw vtkm::cont::ErrorBadValue("Invalid component index to ArrayExtractComponent.");
}
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -10,6 +10,7 @@
#ifndef vtk_m_ArrayHandleCompositeVector_h
#define vtk_m_ArrayHandleCompositeVector_h
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/Deprecated.h>
@ -454,6 +455,68 @@ VTKM_CONT ArrayHandleCompositeVector<ArrayTs...> make_ArrayHandleCompositeVector
(void)checkArrayHandles;
return ArrayHandleCompositeVector<ArrayTs...>(arrays...);
}
//--------------------------------------------------------------------------------
// Specialization of ArrayExtractComponent
namespace internal
{
namespace detail
{
template <typename T>
struct ExtractComponentCompositeVecFunctor
{
using ResultArray = vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>;
ResultArray operator()(vtkm::IdComponent, vtkm::IdComponent, vtkm::CopyFlag) const
{
throw vtkm::cont::ErrorBadValue("Invalid component index given to ArrayExtractComponent.");
}
template <typename A0, typename... As>
ResultArray operator()(vtkm::IdComponent compositeIndex,
vtkm::IdComponent subIndex,
vtkm::CopyFlag allowCopy,
const A0& array0,
const As&... arrays) const
{
if (compositeIndex == 0)
{
return vtkm::cont::internal::ArrayExtractComponentImpl<typename A0::StorageTag>{}(
array0, subIndex, allowCopy);
}
else
{
return (*this)(--compositeIndex, subIndex, allowCopy, arrays...);
}
}
};
} // namespace detail
template <typename... StorageTags>
struct ArrayExtractComponentImpl<StorageTagCompositeVec<StorageTags...>>
{
template <typename T, vtkm::IdComponent NUM_COMPONENTS>
typename detail::ExtractComponentCompositeVecFunctor<T>::ResultArray operator()(
const vtkm::cont::ArrayHandle<vtkm::Vec<T, NUM_COMPONENTS>,
vtkm::cont::StorageTagCompositeVec<StorageTags...>>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
vtkm::cont::ArrayHandleCompositeVector<vtkm::cont::ArrayHandle<T, StorageTags>...> array(src);
constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat<T>::NUM_COMPONENTS;
return array.GetArrayTuple().Apply(detail::ExtractComponentCompositeVecFunctor<T>{},
componentIndex / NUM_SUB_COMPONENTS,
componentIndex % NUM_SUB_COMPONENTS,
allowCopy);
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -10,7 +10,9 @@
#ifndef vtk_m_cont_ArrayHandleExtractComponent_h
#define vtk_m_cont_ArrayHandleExtractComponent_h
#include <vtkm/StaticAssert.h>
#include <vtkm/VecTraits.h>
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
namespace vtkm
@ -228,6 +230,32 @@ VTKM_CONT ArrayHandleExtractComponent<ArrayHandleType> make_ArrayHandleExtractCo
{
return ArrayHandleExtractComponent<ArrayHandleType>(array, component);
}
namespace internal
{
template <typename ArrayHandleType>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagExtractComponent<ArrayHandleType>>
{
auto operator()(const vtkm::cont::ArrayHandleExtractComponent<ArrayHandleType>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
-> decltype(ArrayExtractComponentImpl<typename ArrayHandleType::StorageTag>{}(
std::declval<ArrayHandleType>(),
componentIndex,
allowCopy))
{
using ValueType = typename ArrayHandleType::ValueType;
using ComponentType = typename vtkm::VecTraits<ValueType>::ComponentType;
using FlatComponent = vtkm::VecFlat<ComponentType>;
constexpr vtkm::IdComponent FLAT_SUB_COMPONENTS = FlatComponent::NUM_COMPONENTS;
return ArrayExtractComponentImpl<typename ArrayHandleType::StorageTag>{}(
src.GetArray(), (src.GetComponent() * FLAT_SUB_COMPONENTS) + componentIndex, allowCopy);
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -10,6 +10,7 @@
#ifndef vtk_m_cont_ArrayHandleGroupVec_h
#define vtk_m_cont_ArrayHandleGroupVec_h
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayPortal.h>
#include <vtkm/cont/ErrorBadValue.h>
@ -236,6 +237,45 @@ VTKM_CONT vtkm::cont::ArrayHandleGroupVec<ArrayHandleType, NUM_COMPONENTS> make_
{
return vtkm::cont::ArrayHandleGroupVec<ArrayHandleType, NUM_COMPONENTS>(array);
}
//--------------------------------------------------------------------------------
// Specialization of ArrayExtractComponent
namespace internal
{
template <typename ComponentsStorageTag, vtkm::IdComponent NUM_COMPONENTS>
struct ArrayExtractComponentImpl<
vtkm::cont::StorageTagGroupVec<ComponentsStorageTag, NUM_COMPONENTS>>
{
template <typename T>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> operator()(
const vtkm::cont::ArrayHandle<
vtkm::Vec<T, NUM_COMPONENTS>,
vtkm::cont::StorageTagGroupVec<ComponentsStorageTag, NUM_COMPONENTS>>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
vtkm::cont::ArrayHandleGroupVec<vtkm::cont::ArrayHandle<T, ComponentsStorageTag>,
NUM_COMPONENTS>
srcArray(src);
constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat<T>::NUM_COMPONENTS;
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> dest =
ArrayExtractComponentImpl<ComponentsStorageTag>{}(
srcArray.GetComponentsArray(), componentIndex % NUM_SUB_COMPONENTS, allowCopy);
// Adjust stride and offset to expectations of grouped values
return vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>(
dest.GetBasicArray(),
dest.GetNumberOfValues() / NUM_COMPONENTS,
dest.GetStride() * NUM_COMPONENTS,
dest.GetOffset() + (dest.GetStride() * (componentIndex / NUM_SUB_COMPONENTS)),
dest.GetModulo(),
dest.GetDivisor());
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -13,6 +13,7 @@
#include <vtkm/Assert.h>
#include <vtkm/TypeTraits.h>
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCartesianProduct.h>
#include <vtkm/cont/ArrayHandleCast.h>
@ -436,6 +437,44 @@ using ArrayHandleMultiplexerFromListTag VTKM_DEPRECATED(
template <typename List>
using ArrayHandleMultiplexerFromList = vtkm::ListApply<List, ArrayHandleMultiplexer>;
namespace internal
{
namespace detail
{
struct ArrayExtractComponentMultiplexerFunctor
{
template <typename ArrayType>
auto operator()(const ArrayType& array,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
-> decltype(vtkm::cont::ArrayExtractComponent(array, componentIndex, allowCopy))
{
return vtkm::cont::internal::ArrayExtractComponentImpl<typename ArrayType::StorageTag>{}(
array, componentIndex, allowCopy);
}
};
} // namespace detail
template <typename... Ss>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagMultiplexer<Ss...>>
{
template <typename T>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> operator()(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy)
{
vtkm::cont::ArrayHandleMultiplexer<vtkm::cont::ArrayHandle<T, Ss>...> array(src);
return array.GetArrayHandleVariant().CastAndCall(
detail::ArrayExtractComponentMultiplexerFunctor{}, componentIndex, allowCopy);
}
};
} // namespace internal
} // namespace cont
} // namespace vtkm

@ -11,6 +11,7 @@
#ifndef vtk_m_cont_ArrayHandleReverse_h
#define vtk_m_cont_ArrayHandleReverse_h
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/ErrorBadValue.h>
@ -205,6 +206,39 @@ VTKM_CONT ArrayHandleReverse<HandleType> make_ArrayHandleReverse(const HandleTyp
{
return ArrayHandleReverse<HandleType>(handle);
}
namespace internal
{
template <typename StorageTag>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagReverse<StorageTag>>
{
template <typename T>
using StrideArrayType =
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>;
template <typename T>
StrideArrayType<T> operator()(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagReverse<StorageTag>>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
vtkm::cont::ArrayHandleReverse<vtkm::cont::ArrayHandle<T, StorageTag>> srcArray(src);
StrideArrayType<T> subArray =
ArrayExtractComponentImpl<StorageTag>{}(srcArray.GetSourceArray(), componentIndex, allowCopy);
// Reverse the array by starting at the end and striding backward
return StrideArrayType<T>(subArray.GetBasicArray(),
srcArray.GetNumberOfValues(),
-subArray.GetStride(),
subArray.GetOffset() +
(subArray.GetStride() * (subArray.GetNumberOfValues() - 1)),
subArray.GetModulo(),
subArray.GetDivisor());
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -10,6 +10,7 @@
#ifndef vtk_m_cont_ArrayHandleSOA_h
#define vtk_m_cont_ArrayHandleSOA_h
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/Math.h>
@ -524,6 +525,35 @@ VTKM_CONT ArrayHandleSOA<
vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingArrays) + 1)>>(
length, array0, componentArrays...);
}
namespace internal
{
template <>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagSOA>
{
template <typename T>
auto operator()(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagSOA>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
-> decltype(
ArrayExtractComponentImpl<vtkm::cont::StorageTagBasic>{}(vtkm::cont::ArrayHandleBasic<T>{},
componentIndex,
allowCopy))
{
using FirstLevelComponentType = typename vtkm::VecTraits<T>::ComponentType;
vtkm::cont::ArrayHandleSOA<T> array(src);
constexpr vtkm::IdComponent NUM_SUB_COMPONENTS =
vtkm::VecFlat<FirstLevelComponentType>::NUM_COMPONENTS;
return ArrayExtractComponentImpl<vtkm::cont::StorageTagBasic>{}(
array.GetArray(componentIndex / NUM_SUB_COMPONENTS),
componentIndex % NUM_SUB_COMPONENTS,
allowCopy);
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -44,15 +44,16 @@ struct ArrayStrideInfo
VTKM_EXEC_CONT vtkm::Id ArrayIndex(vtkm::Id index) const
{
vtkm::Id arrayIndex = (index * this->Stride) + this->Offset;
if (this->Modulo > 0)
{
arrayIndex = arrayIndex % this->Modulo;
}
vtkm::Id arrayIndex = index;
if (this->Divisor > 1)
{
arrayIndex = arrayIndex / this->Divisor;
}
if (this->Modulo > 0)
{
arrayIndex = arrayIndex % this->Modulo;
}
arrayIndex = (arrayIndex * this->Stride) + this->Offset;
return arrayIndex;
}
};
@ -123,12 +124,12 @@ public:
return detail::ArrayPortalBasicWriteGet(this->Array + this->Info.ArrayIndex(index));
}
VTKM_EXEC_CONT ValueType Set(vtkm::Id index, const ValueType& value) const
VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const
{
VTKM_ASSERT(index >= 0);
VTKM_ASSERT(index < this->GetNumberOfValues());
return detail::ArrayPortalBasicWriteSet(this->Array + this->Info.ArrayIndex(index), value);
detail::ArrayPortalBasicWriteSet(this->Array + this->Info.ArrayIndex(index), value);
}
VTKM_EXEC_CONT ValueType* GetArray() const { return this->Array; }
@ -229,6 +230,11 @@ public:
{
return vtkm::cont::internal::CreateBuffers(info, sourceBuffer);
}
static vtkm::cont::ArrayHandleBasic<T> GetBasicArray(const vtkm::cont::internal::Buffer* buffers)
{
return vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>(buffers + 1);
}
};
} // namespace internal
@ -248,7 +254,7 @@ VTKM_ARRAY_HANDLE_NEW_STYLE(T, vtkm::cont::StorageTagStride);
/// Optionally, you can also specify a modulo and divisor. If they are specified, the index
/// mangling becomes:
///
/// (((index * stride) + offset) % modulo) / divisor
/// (((index / divisor) % modulo) * stride) + offset
///
/// You can "disable" any of the aforementioned operations by setting them to the following
/// values (most of which are arithmetic identities):
@ -275,6 +281,7 @@ public:
(ArrayHandleStride<T>),
(ArrayHandle<T, vtkm::cont::StorageTagStride>));
private:
using StorageType = vtkm::cont::internal::Storage<ValueType, StorageTag>;
public:
@ -313,6 +320,11 @@ public:
vtkm::Id GetOffset() const { return StorageType::GetInfo(this->GetBuffers()).Offset; }
vtkm::Id GetModulo() const { return StorageType::GetInfo(this->GetBuffers()).Modulo; }
vtkm::Id GetDivisor() const { return StorageType::GetInfo(this->GetBuffers()).Divisor; }
vtkm::cont::ArrayHandleBasic<T> GetBasicArray() const
{
return StorageType::GetBasicArray(this->GetBuffers());
}
};
}

@ -0,0 +1,92 @@
//============================================================================
// 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/ArrayHandleUniformPointCoordinates.h>
namespace vtkm
{
namespace cont
{
ArrayHandleUniformPointCoordinates::ArrayHandleUniformPointCoordinates(vtkm::Id3 dimensions,
ValueType origin,
ValueType spacing)
: Superclass(internal::PortalToArrayHandleImplicitBuffers(
vtkm::internal::ArrayPortalUniformPointCoordinates(dimensions, origin, spacing)))
{
}
ArrayHandleUniformPointCoordinates::~ArrayHandleUniformPointCoordinates() = default;
vtkm::Id3 ArrayHandleUniformPointCoordinates::GetDimensions() const
{
return this->ReadPortal().GetDimensions();
}
vtkm::Vec3f ArrayHandleUniformPointCoordinates::GetOrigin() const
{
return this->ReadPortal().GetOrigin();
}
vtkm::Vec3f ArrayHandleUniformPointCoordinates::GetSpacing() const
{
return this->ReadPortal().GetSpacing();
}
namespace internal
{
vtkm::cont::ArrayHandleStride<vtkm::FloatDefault>
ArrayExtractComponentImpl<vtkm::cont::StorageTagUniformPoints>::operator()(
const vtkm::cont::ArrayHandleUniformPointCoordinates& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
if (allowCopy != vtkm::CopyFlag::On)
{
throw vtkm::cont::ErrorBadValue(
"Cannot extract component of ArrayHandleUniformPointCoordinates without copying. "
"(However, the whole array does not need to be copied.)");
}
vtkm::Id3 dims = src.GetDimensions();
vtkm::Vec3f origin = src.GetOrigin();
vtkm::Vec3f spacing = src.GetSpacing();
// A "slow" way to create the data, but the array is probably short. It would probably take
// longer to schedule something on a device. (Can change that later if use cases change.)
vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault> componentArray;
componentArray.Allocate(dims[componentIndex]);
auto portal = componentArray.WritePortal();
for (vtkm::Id i = 0; i < dims[componentIndex]; ++i)
{
portal.Set(i, origin[componentIndex] + (i * spacing[componentIndex]));
}
switch (componentIndex)
{
case 0:
return vtkm::cont::ArrayHandleStride<vtkm::FloatDefault>(
componentArray, src.GetNumberOfValues(), 1, 0, dims[0], 1);
case 1:
return vtkm::cont::ArrayHandleStride<vtkm::FloatDefault>(
componentArray, src.GetNumberOfValues(), 1, 0, dims[1], dims[0]);
case 2:
return vtkm::cont::ArrayHandleStride<vtkm::FloatDefault>(
componentArray, src.GetNumberOfValues(), 1, 0, 0, dims[0] * dims[1]);
default:
throw vtkm::cont::ErrorBadValue("Bad index given to ArrayExtractComponent.");
}
}
} // namespace internal
}
} // namespace vtkm::cont

@ -10,6 +10,7 @@
#ifndef vtk_m_cont_ArrayHandleUniformPointCoordinates_h
#define vtk_m_cont_ArrayHandleUniformPointCoordinates_h
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandleImplicit.h>
#include <vtkm/internal/ArrayPortalUniformPointCoordinates.h>
@ -44,7 +45,7 @@ VTKM_ARRAY_HANDLE_NEW_STYLE(vtkm::Vec3f, vtkm::cont::StorageTagUniformPoints);
/// uniform orthogonal grid (extent, origin, and spacing) and implicitly
/// computes these coordinates in its array portal.
///
class VTKM_ALWAYS_EXPORT ArrayHandleUniformPointCoordinates
class VTKM_CONT_EXPORT ArrayHandleUniformPointCoordinates
: public vtkm::cont::ArrayHandle<vtkm::Vec3f, vtkm::cont::StorageTagUniformPoints>
{
public:
@ -59,19 +60,34 @@ public:
VTKM_CONT
ArrayHandleUniformPointCoordinates(vtkm::Id3 dimensions,
ValueType origin = ValueType(0.0f, 0.0f, 0.0f),
ValueType spacing = ValueType(1.0f, 1.0f, 1.0f))
: Superclass(internal::PortalToArrayHandleImplicitBuffers(
vtkm::internal::ArrayPortalUniformPointCoordinates(dimensions, origin, spacing)))
{
}
ValueType spacing = ValueType(1.0f, 1.0f, 1.0f));
/// Implemented so that it is defined exclusively in the control environment.
/// If there is a separate device for the execution environment (for example,
/// with CUDA), then the automatically generated destructor could be
/// created for all devices, and it would not be valid for all devices.
///
~ArrayHandleUniformPointCoordinates() {}
~ArrayHandleUniformPointCoordinates();
VTKM_CONT vtkm::Id3 GetDimensions() const;
VTKM_CONT vtkm::Vec3f GetOrigin() const;
VTKM_CONT vtkm::Vec3f GetSpacing() const;
};
namespace internal
{
template <>
struct VTKM_CONT_EXPORT ArrayExtractComponentImpl<vtkm::cont::StorageTagUniformPoints>
{
vtkm::cont::ArrayHandleStride<vtkm::FloatDefault> operator()(
const vtkm::cont::ArrayHandleUniformPointCoordinates& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const;
};
} // namespace internal
}
} // namespace vtkm::cont

@ -13,6 +13,7 @@
#include <vtkm/Assert.h>
#include <vtkm/Deprecated.h>
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayPortal.h>
@ -328,6 +329,10 @@ public:
: Superclass(StorageType(array, startIndex, numValues))
{
}
VTKM_CONT ArrayHandleType GetSourceArray() const { return this->GetStorage().GetArray(); }
VTKM_CONT vtkm::Id GetStartIndex() const { return this->GetStorage().GetStartIndex(); }
};
template <typename ArrayHandleType>
@ -339,6 +344,39 @@ ArrayHandleView<ArrayHandleType> make_ArrayHandleView(const ArrayHandleType& arr
return ArrayHandleView<ArrayHandleType>(array, startIndex, numValues);
}
namespace internal
{
template <typename StorageTag>
struct ArrayExtractComponentImpl<StorageTagView<StorageTag>>
{
template <typename T>
using StrideArrayType =
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>;
template <typename T>
StrideArrayType<T> operator()(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagView<StorageTag>>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
vtkm::cont::ArrayHandleView<vtkm::cont::ArrayHandle<T, StorageTag>> srcArray(src);
StrideArrayType<T> subArray =
ArrayExtractComponentImpl<StorageTag>{}(srcArray.GetSourceArray(), componentIndex, allowCopy);
// Narrow the array by adjusting the size and offset.
return StrideArrayType<T>(subArray.GetBasicArray(),
srcArray.GetNumberOfValues(),
subArray.GetStride(),
subArray.GetOffset() +
(subArray.GetStride() * srcArray.GetStartIndex()),
subArray.GetModulo(),
subArray.GetDivisor());
}
};
} // namespace internal
}
} // namespace vtkm::cont

@ -11,6 +11,7 @@
set(headers
Algorithm.h
ArrayCopy.h
ArrayExtractComponent.h
ArrayGetValues.h
ArrayHandle.h
ArrayHandleBasic.h
@ -139,6 +140,7 @@ set(sources
ArrayHandleBasic.cxx
ArrayHandleSOA.cxx
ArrayHandleStride.cxx
ArrayHandleUniformPointCoordinates.cxx
BitField.cxx
ColorTablePresets.cxx
DeviceAdapterTag.cxx

@ -34,6 +34,7 @@ vtkm_declare_headers(${headers})
set(unit_tests
UnitTestAlgorithm.cxx
UnitTestArrayCopy.cxx
UnitTestArrayExtractComponent.cxx
UnitTestArrayGetValues.cxx
UnitTestArrayHandleCartesianProduct.cxx
UnitTestArrayHandleCompositeVector.cxx

@ -0,0 +1,250 @@
//============================================================================
// 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/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandleCartesianProduct.h>
#include <vtkm/cont/ArrayHandleCompositeVector.h>
#include <vtkm/cont/ArrayHandleExtractComponent.h>
#include <vtkm/cont/ArrayHandleGroupVec.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandleMultiplexer.h>
#include <vtkm/cont/ArrayHandleReverse.h>
#include <vtkm/cont/ArrayHandleSOA.h>
#include <vtkm/cont/ArrayHandleUniformPointCoordinates.h>
#include <vtkm/cont/ArrayHandleView.h>
#include <vtkm/VecFlat.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
constexpr vtkm::Id ARRAY_SIZE = 10;
template <typename T, typename S>
void CheckInputArray(const vtkm::cont::ArrayHandle<T, S>& originalArray,
vtkm::CopyFlag allowCopy = vtkm::CopyFlag::Off)
{
//std::cout << " Checking input array type "
// << vtkm::cont::TypeToString<vtkm::cont::ArrayHandle<T, S>>() << std::endl;
//std::cout << " Original array: ";
//vtkm::cont::printSummary_ArrayHandle(originalArray, std::cout);
using FlatVec = vtkm::VecFlat<T>;
using ComponentType = typename FlatVec::ComponentType;
for (vtkm::IdComponent componentId = 0; componentId < FlatVec::NUM_COMPONENTS; ++componentId)
{
vtkm::cont::ArrayHandleStride<ComponentType> componentArray =
vtkm::cont::ArrayExtractComponent(originalArray, componentId, allowCopy);
//std::cout << " Component " << componentId << ": ";
//vtkm::cont::printSummary_ArrayHandle(componentArray, std::cout);
auto originalPortal = originalArray.ReadPortal();
auto componentPortal = componentArray.ReadPortal();
VTKM_TEST_ASSERT(originalPortal.GetNumberOfValues() == componentPortal.GetNumberOfValues());
for (vtkm::Id arrayIndex = 0; arrayIndex < originalArray.GetNumberOfValues(); ++arrayIndex)
{
auto originalValue = vtkm::make_VecFlat(originalPortal.Get(arrayIndex));
ComponentType componentValue = componentPortal.Get(arrayIndex);
VTKM_TEST_ASSERT(test_equal(originalValue[componentId], componentValue));
}
}
}
template <typename T, typename S>
void CheckOutputArray(const vtkm::cont::ArrayHandle<T, S>& originalArray)
{
CheckInputArray(originalArray);
//std::cout << " Checking output array type "
// << vtkm::cont::TypeToString<vtkm::cont::ArrayHandle<T, S>>() << std::endl;
//std::cout << " Original array: ";
//vtkm::cont::printSummary_ArrayHandle(originalArray, std::cout);
vtkm::cont::ArrayHandle<T, S> outputArray;
outputArray.Allocate(originalArray.GetNumberOfValues());
using FlatVec = vtkm::VecFlat<T>;
using ComponentType = typename FlatVec::ComponentType;
constexpr vtkm::IdComponent numComponents = FlatVec::NUM_COMPONENTS;
for (vtkm::IdComponent componentId = 0; componentId < numComponents; ++componentId)
{
vtkm::cont::ArrayHandleStride<ComponentType> inComponentArray =
vtkm::cont::ArrayExtractComponent(originalArray, numComponents - componentId - 1);
vtkm::cont::ArrayHandleStride<ComponentType> outComponentArray =
vtkm::cont::ArrayExtractComponent(outputArray, componentId, vtkm::CopyFlag::Off);
auto inPortal = inComponentArray.ReadPortal();
auto outPortal = outComponentArray.WritePortal();
VTKM_TEST_ASSERT(inComponentArray.GetNumberOfValues() == originalArray.GetNumberOfValues());
VTKM_TEST_ASSERT(outComponentArray.GetNumberOfValues() == originalArray.GetNumberOfValues());
for (vtkm::Id arrayIndex = 0; arrayIndex < originalArray.GetNumberOfValues(); ++arrayIndex)
{
outPortal.Set(arrayIndex, inPortal.Get(arrayIndex));
}
}
//std::cout << " Output array: ";
//vtkm::cont::printSummary_ArrayHandle(outputArray, std::cout);
auto inPortal = originalArray.ReadPortal();
auto outPortal = outputArray.ReadPortal();
for (vtkm::Id arrayIndex = 0; arrayIndex < originalArray.GetNumberOfValues(); ++arrayIndex)
{
FlatVec inValue = vtkm::make_VecFlat(inPortal.Get(arrayIndex));
FlatVec outValue = vtkm::make_VecFlat(outPortal.Get(arrayIndex));
for (vtkm::IdComponent componentId = 0; componentId < numComponents; ++componentId)
{
VTKM_TEST_ASSERT(test_equal(inValue[componentId], outValue[numComponents - componentId - 1]));
}
}
}
void DoTest()
{
using ArrayMultiplexerType =
vtkm::cont::ArrayHandleMultiplexer<vtkm::cont::ArrayHandleBasic<vtkm::Vec3f>,
vtkm::cont::ArrayHandleSOA<vtkm::Vec3f>>;
{
std::cout << "Basic array" << std::endl;
vtkm::cont::ArrayHandle<vtkm::Vec3f> array;
array.Allocate(ARRAY_SIZE);
SetPortal(array.WritePortal());
CheckOutputArray(array);
std::cout << "ArrayHandleExtractComponent" << std::endl;
CheckOutputArray(vtkm::cont::make_ArrayHandleExtractComponent(array, 1));
std::cout << "ArrayHandleMultiplexer" << std::endl;
CheckInputArray(ArrayMultiplexerType(array));
}
{
std::cout << "SOA array" << std::endl;
vtkm::cont::ArrayHandleSOA<vtkm::Vec3f> array;
array.Allocate(ARRAY_SIZE);
SetPortal(array.WritePortal());
CheckOutputArray(array);
CheckInputArray(ArrayMultiplexerType(array));
}
{
std::cout << "Stride array" << std::endl;
constexpr vtkm::Id STRIDE = 7;
vtkm::cont::ArrayHandleBasic<vtkm::Vec3f> originalArray;
originalArray.Allocate(ARRAY_SIZE * STRIDE);
for (vtkm::Id offset = 0; offset < STRIDE; ++offset)
{
vtkm::cont::ArrayHandleStride<vtkm::Vec3f> strideArray(
originalArray, ARRAY_SIZE, STRIDE, offset);
CheckOutputArray(strideArray);
}
}
{
std::cout << "ArrayHandleGroupVec" << std::endl;
vtkm::cont::ArrayHandle<vtkm::Vec3f> array;
array.Allocate(ARRAY_SIZE * 2);
SetPortal(array.WritePortal());
CheckOutputArray(vtkm::cont::make_ArrayHandleGroupVec<2>(array));
}
{
std::cout << "ArrayHandleCompositeVector" << std::endl;
vtkm::cont::ArrayHandle<vtkm::Vec3f> array0;
vtkm::cont::ArrayHandle<vtkm::Vec3f> array1;
array0.Allocate(ARRAY_SIZE);
array1.Allocate(ARRAY_SIZE);
SetPortal(array0.WritePortal());
SetPortal(array1.WritePortal());
auto compositeArray = vtkm::cont::make_ArrayHandleCompositeVector(array0, array1);
CheckOutputArray(compositeArray);
CheckOutputArray(vtkm::cont::make_ArrayHandleExtractComponent(compositeArray, 1));
}
{
std::cout << "ArrayHandleCartesianProduct" << std::endl;
vtkm::cont::ArrayHandle<vtkm::Float64> array0;
vtkm::cont::ArrayHandle<vtkm::Float64> array1;
vtkm::cont::ArrayHandle<vtkm::Float64> array2;
array0.Allocate(ARRAY_SIZE);
array1.Allocate(ARRAY_SIZE / 2);
array2.Allocate(ARRAY_SIZE + 2);
SetPortal(array0.WritePortal());
SetPortal(array1.WritePortal());
SetPortal(array2.WritePortal());
CheckInputArray(vtkm::cont::make_ArrayHandleCartesianProduct(array0, array1, array2));
}
{
std::cout << "ArrayHandleUniformPointCoordinates" << std::endl;
vtkm::cont::ArrayHandleUniformPointCoordinates array(
vtkm::Id3{ ARRAY_SIZE, ARRAY_SIZE + 2, ARRAY_SIZE / 2 });
CheckInputArray(array, vtkm::CopyFlag::On);
}
{
std::cout << "ArrayHandleReverse" << std::endl;
vtkm::cont::ArrayHandle<vtkm::Vec3f> array;
array.Allocate(ARRAY_SIZE);
SetPortal(array.WritePortal());
CheckOutputArray(vtkm::cont::make_ArrayHandleReverse(array));
}
{
std::cout << "ArrayHandleView" << std::endl;
vtkm::cont::ArrayHandle<vtkm::Vec3f> array;
array.Allocate(ARRAY_SIZE);
SetPortal(array.WritePortal());
CheckInputArray(
vtkm::cont::make_ArrayHandleView(array, (ARRAY_SIZE / 3), (ARRAY_SIZE / 3) + 1));
}
{
std::cout << "ArrayHandleIndex" << std::endl;
vtkm::cont::ArrayHandleIndex array(ARRAY_SIZE);
CheckInputArray(array, vtkm::CopyFlag::On);
}
{
std::cout << "Weird combination." << std::endl;
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Vec4f, 2>> base0;
base0.Allocate(ARRAY_SIZE);
SetPortal(base0.WritePortal());
vtkm::cont::ArrayHandleSOA<vtkm::Vec4f> base1_sub;
base1_sub.Allocate(ARRAY_SIZE);
SetPortal(base1_sub.WritePortal());
auto base1 = vtkm::cont::make_ArrayHandleGroupVec<2>(base1_sub);
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Vec4f, 2>> base2_sub;
base2_sub.Allocate(ARRAY_SIZE + 10);
SetPortal(base2_sub.WritePortal());
auto base2 = vtkm::cont::make_ArrayHandleView(base2_sub, 2, ARRAY_SIZE + 4);
auto array = vtkm::cont::make_ArrayHandleCartesianProduct(base0, base1, base2);
CheckInputArray(array);
}
}
} // anonymous namespace
int UnitTestArrayExtractComponent(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(DoTest, argc, argv);
}