vtk-m/vtkm/cont/ArrayHandleRuntimeVec.h
Sujin Philip b0715e14af ArrayHandle::StorageType should be public
StorageType is public in vtkm::cont::ArrayHandle, but some subclasses  
re-declare it as private. Having it public provides a convenient way to  
get the storage type of an ArrayHandle, otherwise it is quite verbose.

Addresses: #314
2023-04-14 11:26:37 -04:00

504 lines
17 KiB
C++

//============================================================================
// 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_ArrayHandleRuntimeVec_h
#define vtk_m_cont_ArrayHandleRuntimeVec_h
#include <vtkm/cont/ArrayExtractComponent.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleBasic.h>
#include <vtkm/cont/ArrayPortal.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/Assert.h>
#include <vtkm/StaticAssert.h>
#include <vtkm/VecFromPortal.h>
#include <vtkm/VecTraits.h>
namespace vtkm
{
namespace internal
{
namespace detail
{
template <typename T>
struct UnrollVecImpl
{
using type = vtkm::Vec<T, 1>;
};
template <typename T, vtkm::IdComponent N>
struct UnrollVecImpl<vtkm::Vec<T, N>>
{
using subtype = typename UnrollVecImpl<T>::type;
using type = vtkm::Vec<typename subtype::ComponentType, subtype::NUM_COMPONENTS * N>;
};
} // namespace detail
// A helper class that unrolls a nested `Vec` to a single layer `Vec`. This is similar
// to `vtkm::VecFlat`, except that this only flattens `vtkm::Vec<T,N>` objects, and not
// any other `Vec`-like objects. The reason is that a `vtkm::Vec<T,N>` is the same as N
// consecutive `T` objects whereas the same may not be said about other `Vec`-like objects.
template <typename T>
using UnrollVec = typename detail::UnrollVecImpl<T>::type;
template <typename ComponentsPortalType>
class VTKM_ALWAYS_EXPORT ArrayPortalRuntimeVec
{
public:
using ComponentType = typename std::remove_const<typename ComponentsPortalType::ValueType>::type;
using ValueType = vtkm::VecFromPortal<ComponentsPortalType>;
ArrayPortalRuntimeVec() = default;
VTKM_EXEC_CONT ArrayPortalRuntimeVec(const ComponentsPortalType& componentsPortal,
vtkm::IdComponent numComponents)
: ComponentsPortal(componentsPortal)
, NumberOfComponents(numComponents)
{
}
/// Copy constructor for any other ArrayPortalRuntimeVec with a portal type
/// that can be copied to this portal type. This allows us to do any type
/// casting that the portals do (like the non-const to const cast).
template <typename OtherComponentsPortalType>
VTKM_EXEC_CONT ArrayPortalRuntimeVec(const ArrayPortalRuntimeVec<OtherComponentsPortalType>& src)
: ComponentsPortal(src.GetComponentsPortal())
, NumberOfComponents(src.GetNumberOfComponents())
{
}
VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const
{
return this->ComponentsPortal.GetNumberOfValues() / this->NumberOfComponents;
}
VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const
{
return ValueType(
this->ComponentsPortal, this->NumberOfComponents, index * this->NumberOfComponents);
}
VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const
{
if ((&value.GetPortal() == &this->ComponentsPortal) &&
(value.GetOffset() == (index * this->NumberOfComponents)))
{
// The ValueType (VecFromPortal) operates on demand. Thus, if you set
// something in the value, it has already been passed to the array.
}
else
{
// The value comes from somewhere else. Copy data in.
this->Get(index) = value;
}
}
VTKM_EXEC_CONT const ComponentsPortalType& GetComponentsPortal() const
{
return this->ComponentsPortal;
}
VTKM_EXEC_CONT vtkm::IdComponent GetNumberOfComponents() const
{
return this->NumberOfComponents;
}
private:
ComponentsPortalType ComponentsPortal;
vtkm::IdComponent NumberOfComponents = 0;
};
}
} // namespace vtkm::internal
namespace vtkm
{
namespace cont
{
struct VTKM_ALWAYS_EXPORT StorageTagRuntimeVec
{
};
namespace internal
{
template <typename ComponentsPortal>
class Storage<vtkm::VecFromPortal<ComponentsPortal>, vtkm::cont::StorageTagRuntimeVec>
{
using ComponentType = typename ComponentsPortal::ValueType;
using ComponentsStorage =
vtkm::cont::internal::Storage<ComponentType, vtkm::cont::StorageTagBasic>;
VTKM_STATIC_ASSERT_MSG(
vtkm::VecTraits<ComponentType>::NUM_COMPONENTS == 1,
"ArrayHandleRuntimeVec only supports scalars grouped into a single Vec. Nested Vecs can "
"still be used with ArrayHandleRuntimeVec. The values are treated as flattened (like "
"with VecFlat).");
using ComponentsArray = vtkm::cont::ArrayHandle<ComponentType, StorageTagBasic>;
VTKM_STATIC_ASSERT_MSG(
(std::is_same<ComponentsPortal, typename ComponentsStorage::WritePortalType>::value),
"Used invalid ComponentsPortal type with expected ComponentsStorageTag.");
struct Info
{
vtkm::IdComponent NumberOfComponents;
};
VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> ComponentsBuffers(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return std::vector<vtkm::cont::internal::Buffer>(buffers.begin() + 1, buffers.end());
}
public:
using ReadPortalType =
vtkm::internal::ArrayPortalRuntimeVec<typename ComponentsStorage::ReadPortalType>;
using WritePortalType =
vtkm::internal::ArrayPortalRuntimeVec<typename ComponentsStorage::WritePortalType>;
VTKM_CONT static vtkm::IdComponent GetNumberOfComponents(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return buffers[0].GetMetaData<Info>().NumberOfComponents;
}
VTKM_CONT static vtkm::Id GetNumberOfValues(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return ComponentsStorage::GetNumberOfValues(ComponentsBuffers(buffers)) /
GetNumberOfComponents(buffers);
}
VTKM_CONT static void ResizeBuffers(vtkm::Id numValues,
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token)
{
ComponentsStorage::ResizeBuffers(
numValues * GetNumberOfComponents(buffers), ComponentsBuffers(buffers), preserve, token);
}
VTKM_CONT static void Fill(const std::vector<vtkm::cont::internal::Buffer>&,
const vtkm::VecFromPortal<ComponentsPortal>&,
vtkm::Id,
vtkm::Id,
vtkm::cont::Token&)
{
throw vtkm::cont::ErrorBadType("Fill not supported for ArrayHandleRuntimeVec.");
}
VTKM_CONT static ReadPortalType CreateReadPortal(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
{
return ReadPortalType(
ComponentsStorage::CreateReadPortal(ComponentsBuffers(buffers), device, token),
GetNumberOfComponents(buffers));
}
VTKM_CONT static WritePortalType CreateWritePortal(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
{
return WritePortalType(
ComponentsStorage::CreateWritePortal(ComponentsBuffers(buffers), device, token),
GetNumberOfComponents(buffers));
}
VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> CreateBuffers(
vtkm::IdComponent numComponents = 1,
const ComponentsArray& componentsArray = ComponentsArray{})
{
VTKM_LOG_IF_S(vtkm::cont::LogLevel::Warn,
(componentsArray.GetNumberOfValues() % numComponents) != 0,
"Array given to ArrayHandleRuntimeVec has size ("
<< componentsArray.GetNumberOfValues()
<< ") that is not divisible by the number of components selected ("
<< numComponents << ").");
Info info;
info.NumberOfComponents = numComponents;
return vtkm::cont::internal::CreateBuffers(info, componentsArray);
}
VTKM_CONT static ComponentsArray GetComponentsArray(
const std::vector<vtkm::cont::internal::Buffer>& buffers)
{
return ComponentsArray(ComponentsBuffers(buffers));
}
VTKM_CONT static void AsArrayHandleBasic(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagBasic>& dest)
{
if (GetNumberOfComponents(buffers) != 1)
{
throw vtkm::cont::ErrorBadType(
"Attempted to pull a scalar array from an ArrayHandleRuntime that does not hold scalars.");
}
dest = GetComponentsArray(buffers);
}
template <vtkm::IdComponent N>
VTKM_CONT static void AsArrayHandleBasic(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::ArrayHandle<vtkm::Vec<ComponentType, N>, vtkm::cont::StorageTagBasic>& dest)
{
if (GetNumberOfComponents(buffers) != N)
{
throw vtkm::cont::ErrorBadType(
"Attempted to pull an array of Vecs of the wrong size from an ArrayHandleRuntime.");
}
dest = vtkm::cont::ArrayHandle<vtkm::Vec<ComponentType, N>, vtkm::cont::StorageTagBasic>(
ComponentsBuffers(buffers));
}
template <typename T, vtkm::IdComponent NInner, vtkm::IdComponent NOuter>
VTKM_CONT static void AsArrayHandleBasic(
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Vec<T, NInner>, NOuter>, vtkm::cont::StorageTagBasic>&
dest)
{
// Flatten the Vec by one level and attempt to get the array handle for that.
vtkm::cont::ArrayHandleBasic<vtkm::Vec<T, NInner * NOuter>> squashedArray;
AsArrayHandleBasic(buffers, squashedArray);
// Now unsquash the array by stealling the buffers and creating an array of the right type
dest =
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Vec<T, NInner>, NOuter>, vtkm::cont::StorageTagBasic>(
squashedArray.GetBuffers());
}
};
} // namespace internal
/// \brief Fancy array handle for a basic array with runtime selected vec size.
///
/// It is sometimes the case that you need to create an array of `Vec`s where
/// the number of components is not known until runtime. This is problematic
/// for normal `ArrayHandle`s because you have to specify the size of the `Vec`s
/// as a template parameter at compile time. `ArrayHandleRuntimeVec` can be used
/// in this case.
///
/// Note that caution should be used with `ArrayHandleRuntimeVec` because the
/// size of the `Vec` values is not known at compile time. Thus, the value
/// type of this array is forced to a special `VecFromPortal` class that can cause
/// surprises if treated as a `Vec`. In particular, the static `NUM_COMPONENTS`
/// expression does not exist. Furthermore, new variables of type `VecFromPortal`
/// cannot be created. This means that simple operators like `+` will not work
/// because they require an intermediate object to be created. (Equal operators
/// like `+=` do work because they are given an existing variable to place the
/// output.)
///
/// It is possible to provide an `ArrayHandleBasic` of the same component
/// type as the underlying storage for this array. In this case, the array
/// will be accessed much in the same manner as `ArrayHandleGroupVec`.
///
/// `ArrayHandleRuntimeVec` also allows you to convert the array to an
/// `ArrayHandleBasic` of the appropriate `Vec` type (or `component` type).
/// A runtime check will be performed to make sure the number of components
/// matches.
///
template <typename ComponentType>
class ArrayHandleRuntimeVec
: public vtkm::cont::ArrayHandle<
vtkm::VecFromPortal<typename ArrayHandleBasic<ComponentType>::WritePortalType>,
vtkm::cont::StorageTagRuntimeVec>
{
public:
VTKM_ARRAY_HANDLE_SUBCLASS(
ArrayHandleRuntimeVec,
(ArrayHandleRuntimeVec<ComponentType>),
(vtkm::cont::ArrayHandle<
vtkm::VecFromPortal<typename ArrayHandleBasic<ComponentType>::WritePortalType>,
vtkm::cont::StorageTagRuntimeVec>));
private:
using ComponentsArrayType = vtkm::cont::ArrayHandle<ComponentType, StorageTagBasic>;
public:
VTKM_CONT
ArrayHandleRuntimeVec(vtkm::IdComponent numComponents,
const ComponentsArrayType& componentsArray = ComponentsArrayType{})
: Superclass(StorageType::CreateBuffers(numComponents, componentsArray))
{
}
VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const
{
return StorageType::GetNumberOfComponents(this->GetBuffers());
}
VTKM_CONT vtkm::cont::ArrayHandleBasic<ComponentType> GetComponentsArray() const
{
return StorageType::GetComponentsArray(this->GetBuffers());
}
///@{
/// \brief Converts the array to that of a basic array handle.
///
/// This method converts the `ArrayHandleRuntimeVec` to a simple `ArrayHandleBasic`.
/// This is useful if the `ArrayHandleRuntimeVec` is passed to a routine that works
/// on an array of a specific `Vec` size (or scalars). After a runtime check, the
/// array can be converted to a typical array and used as such.
template <typename ValueType>
void AsArrayHandleBasic(vtkm::cont::ArrayHandle<ValueType>& array) const
{
StorageType::AsArrayHandleBasic(this->GetBuffers(), array);
}
template <typename ArrayType>
ArrayType AsArrayHandleBasic() const
{
ArrayType array;
this->AsArrayHandleBasic(array);
return array;
}
///@}
};
/// \c make_ArrayHandleRuntimeVec is convenience function to generate an
/// ArrayHandleRuntimeVec. It takes in an ArrayHandle of values and an
/// array handle of offsets and returns an array handle with consecutive
/// entries grouped in a Vec.
///
template <typename T>
VTKM_CONT auto make_ArrayHandleRuntimeVec(
vtkm::IdComponent numComponents,
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>& componentsArray =
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>{})
{
using UnrolledVec = vtkm::internal::UnrollVec<T>;
using ComponentType = typename UnrolledVec::ComponentType;
// Use some dangerous magic to convert the basic array to its base component and create
// an ArrayHandleRuntimeVec from that.
vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagBasic> flatComponents(
componentsArray.GetBuffers());
return vtkm::cont::ArrayHandleRuntimeVec<ComponentType>(
numComponents * UnrolledVec::NUM_COMPONENTS, flatComponents);
}
template <typename T>
VTKM_CONT auto make_ArrayHandleRuntimeVec(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>& componentsArray)
{
return make_ArrayHandleRuntimeVec(1, componentsArray);
}
namespace internal
{
template <>
struct ArrayExtractComponentImpl<vtkm::cont::StorageTagRuntimeVec>
{
template <typename T>
auto operator()(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagRuntimeVec>& src,
vtkm::IdComponent componentIndex,
vtkm::CopyFlag allowCopy) const
{
using ComponentType = typename T::ComponentType;
vtkm::cont::ArrayHandleRuntimeVec<ComponentType> array{ src };
constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat<ComponentType>::NUM_COMPONENTS;
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> dest =
ArrayExtractComponentImpl<vtkm::cont::StorageTagBasic>{}(
array.GetComponentsArray(), componentIndex % NUM_SUB_COMPONENTS, allowCopy);
// Adjust stride and offset to expectations of grouped values
const vtkm::IdComponent numComponents = array.GetNumberOfComponents();
return vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType>(
dest.GetBasicArray(),
dest.GetNumberOfValues() / numComponents,
dest.GetStride() * numComponents,
dest.GetOffset() + (dest.GetStride() * (componentIndex / NUM_SUB_COMPONENTS)),
dest.GetModulo(),
dest.GetDivisor());
}
};
} // namespace internal
}
} // namespace vtkm::cont
//=============================================================================
// Specializations of serialization related classes
/// @cond SERIALIZATION
namespace vtkm
{
namespace cont
{
template <typename T>
struct SerializableTypeString<vtkm::cont::ArrayHandleRuntimeVec<T>>
{
static VTKM_CONT const std::string& Get()
{
static std::string name = "AH_RuntimeVec<" + SerializableTypeString<T>::Get() + ">";
return name;
}
};
template <typename VecType>
struct SerializableTypeString<vtkm::cont::ArrayHandle<VecType, vtkm::cont::StorageTagRuntimeVec>>
: SerializableTypeString<vtkm::cont::ArrayHandleRuntimeVec<typename VecType::ComponentType>>
{
};
}
} // vtkm::cont
namespace mangled_diy_namespace
{
template <typename T>
struct Serialization<vtkm::cont::ArrayHandleRuntimeVec<T>>
{
private:
using Type = vtkm::cont::ArrayHandleRuntimeVec<T>;
using BaseType = vtkm::cont::ArrayHandle<typename Type::ValueType, typename Type::StorageTag>;
public:
static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj)
{
vtkmdiy::save(bb, Type(obj).GetNumberOfComponents());
vtkmdiy::save(bb, Type(obj).GetComponentsArray());
}
static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj)
{
vtkm::IdComponent numComponents;
vtkm::cont::ArrayHandleBasic<T> componentArray;
vtkmdiy::load(bb, numComponents);
vtkmdiy::load(bb, componentArray);
obj = vtkm::cont::make_ArrayHandleRuntimeVec(numComponents, componentArray);
}
};
template <typename VecType>
struct Serialization<vtkm::cont::ArrayHandle<VecType, vtkm::cont::StorageTagRuntimeVec>>
: Serialization<vtkm::cont::ArrayHandleRuntimeVec<typename VecType::ComponentType>>
{
};
} // diy
/// @endcond SERIALIZATION
#endif //vtk_m_cont_ArrayHandleRuntimeVec_h