Add ArrayHandleSwizzle for reordering/removing components from a vec array.

This is tangentially related to #43.
This commit is contained in:
Allison Vacanti 2017-07-28 14:55:37 -04:00
parent c077f16fb9
commit 030ef57380
4 changed files with 927 additions and 0 deletions

@ -0,0 +1,525 @@
//=============================================================================
//
// 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.
//
// Copyright 2017 Sandia Corporation.
// Copyright 2017 UT-Battelle, LLC.
// Copyright 2017 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//
//=============================================================================
#ifndef vtk_m_cont_ArrayHandleSwizzle_h
#define vtk_m_cont_ArrayHandleSwizzle_h
#include <vtkm/VecTraits.h>
#include <vtkm/cont/ArrayHandle.h>
#include <array>
namespace vtkm
{
namespace cont
{
namespace internal
{
// If TestValue appears more than once in ComponentMap, IsUnique will be false,
// but true if TestValue is unique.
template <vtkm::IdComponent TestValue, vtkm::IdComponent... ComponentMap>
struct ComponentIsUnique;
// Terminal case:
template <vtkm::IdComponent TestValue, vtkm::IdComponent Head>
struct ComponentIsUnique<TestValue, Head>
{
const static bool IsUnique = TestValue != Head;
};
// Recursive case:
template <vtkm::IdComponent TestValue, vtkm::IdComponent Head, vtkm::IdComponent... Tail>
struct ComponentIsUnique<TestValue, Head, Tail...>
{
using Next = ComponentIsUnique<TestValue, Tail...>;
const static bool IsUnique = TestValue != Head && Next::IsUnique;
};
// Validate the component map.
// All elements must be (1) unique, (2) >= 0, and (3) < InputSize
template <vtkm::IdComponent InputSize, vtkm::IdComponent... ComponentMap>
struct ValidateComponentMap;
// Terminal impl:
template <vtkm::IdComponent InputSize, vtkm::IdComponent Head>
struct ValidateComponentMap<InputSize, Head>
{
static const bool Valid = Head >= 0 && Head < InputSize;
};
// Recursive impl:
template <vtkm::IdComponent InputSize, vtkm::IdComponent Head, vtkm::IdComponent... Tail>
struct ValidateComponentMap<InputSize, Head, Tail...>
{
using Next = ValidateComponentMap<InputSize, Tail...>;
static const bool IsUnique = ComponentIsUnique<Head, Tail...>::IsUnique;
static const bool Valid = Head >= 0 && Head < InputSize && IsUnique && Next::Valid;
};
} // end namespace internal
/// This class collects metadata for an ArrayHandleSwizzle.
template <typename InputValueType, vtkm::IdComponent... ComponentMap>
struct ArrayHandleSwizzleTraits
{
/// The number of elements in the ComponentMap.
static const vtkm::IdComponent COUNT = static_cast<vtkm::IdComponent>(sizeof...(ComponentMap));
VTKM_STATIC_ASSERT_MSG(COUNT > 0, "Invalid ComponentMap: Cannot swizzle zero components.");
/// A std::array containing the ComponentMap for runtime querying.
using RuntimeComponentMapType = std::array<vtkm::IdComponent, COUNT>;
static VTKM_CONSTEXPR RuntimeComponentMapType GenerateRuntimeComponentMap()
{
return RuntimeComponentMapType{ { ComponentMap... } };
}
/// The ValueType of the ArrayHandleSwizzle's internal ArrayHandle.
using InputType = InputValueType;
/// The VecTraits for InputType.
using InputTraits = VecTraits<InputType>;
using Validator = internal::ValidateComponentMap<InputTraits::NUM_COMPONENTS, ComponentMap...>;
VTKM_STATIC_ASSERT_MSG(Validator::Valid,
"Invalid ComponentMap: Ids in ComponentMap must be unique, positive, and "
"less than the number of input components.");
/// The ComponentType of the ArrayHandleSwizzle.
using ComponentType = typename InputTraits::ComponentType;
/// The ValueType of the ArrayHandleSwizzle.
using OutputType = vtkm::Vec<ComponentType, COUNT>;
// The VecTraits for OutputType.
using OutputTraits = VecTraits<OutputType>;
/// If true, we use all components in the input vector. If false, we'll need
/// to make sure to preserve existing values on write.
static const bool ALL_COMPS_USED = InputTraits::NUM_COMPONENTS == COUNT;
private:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent... Map>
struct GetImpl;
// Terminal case:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent Head>
struct GetImpl<OutputIndex, Head>
{
VTKM_CONSTEXPR vtkm::IdComponent operator()() const { return OutputIndex == 0 ? Head : -1; }
};
// Recursive case:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent Head, vtkm::IdComponent... Tail>
struct GetImpl<OutputIndex, Head, Tail...>
{
using Next = GetImpl<OutputIndex - 1, Tail...>;
VTKM_CONSTEXPR vtkm::IdComponent operator()() const
{
return OutputIndex == 0 ? Head : Next()();
}
};
public:
/// Get the component from ComponentMap at the specified index as a
/// compile-time constant:
template <vtkm::IdComponent OutputIndex>
static VTKM_CONSTEXPR vtkm::IdComponent Get()
{
return GetImpl<OutputIndex, ComponentMap...>()();
}
private:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent... Map>
struct SwizzleImpl;
// Terminal case:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent Head>
struct SwizzleImpl<OutputIndex, Head>
{
static const vtkm::IdComponent InputIndex = Head;
void operator()(const InputType& in, OutputType& out) const
{
OutputTraits::SetComponent(out, OutputIndex, InputTraits::GetComponent(in, InputIndex));
}
};
// Recursive case:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent Head, vtkm::IdComponent... Tail>
struct SwizzleImpl<OutputIndex, Head, Tail...>
{
using Next = SwizzleImpl<OutputIndex + 1, Tail...>;
static const vtkm::IdComponent InputIndex = Head;
void operator()(const InputType& in, OutputType& out) const
{
OutputTraits::SetComponent(out, OutputIndex, InputTraits::GetComponent(in, InputIndex));
Next()(in, out);
}
};
public:
/// Swizzle the input type into the output type.
static void Swizzle(const InputType& in, OutputType& out)
{
SwizzleImpl<0, ComponentMap...>()(in, out);
}
// UnSwizzle output type --> input type
private:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent... Map>
struct UnSwizzleImpl;
// Terminal case:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent Head>
struct UnSwizzleImpl<OutputIndex, Head>
{
static const vtkm::IdComponent InputIndex = Head;
void operator()(const OutputType& out, InputType& in) const
{
InputTraits::SetComponent(in, InputIndex, OutputTraits::GetComponent(out, OutputIndex));
}
};
// Recursive case:
template <vtkm::IdComponent OutputIndex, vtkm::IdComponent Head, vtkm::IdComponent... Tail>
struct UnSwizzleImpl<OutputIndex, Head, Tail...>
{
using Next = UnSwizzleImpl<OutputIndex + 1, Tail...>;
static const vtkm::IdComponent InputIndex = Head;
void operator()(const OutputType& out, InputType& in) const
{
InputTraits::SetComponent(in, InputIndex, OutputTraits::GetComponent(out, OutputIndex));
Next()(out, in);
}
};
// Entry point:
public:
/// Unswizzle the output type back into the input type.
/// @warning If the entire vector is not used, there may be uninitialized
/// data in the resulting InputType vector. See ALL_COMPS_USED flag.
static void UnSwizzle(const OutputType& out, InputType& in)
{
UnSwizzleImpl<0, ComponentMap...>()(out, in);
}
};
namespace internal
{
template <typename PortalType, vtkm::IdComponent... ComponentMap>
class VTKM_ALWAYS_EXPORT ArrayPortalSwizzle
{
using Traits = ArrayHandleSwizzleTraits<typename PortalType::ValueType, ComponentMap...>;
public:
using ValueType = typename Traits::OutputType;
VTKM_EXEC_CONT
ArrayPortalSwizzle()
: Portal()
{
}
VTKM_EXEC_CONT
ArrayPortalSwizzle(const PortalType& portal)
: Portal(portal)
{
}
// Copy constructor
VTKM_EXEC_CONT ArrayPortalSwizzle(const ArrayPortalSwizzle<PortalType, ComponentMap...>& src)
: Portal(src.GetPortal())
{
}
VTKM_EXEC_CONT
vtkm::Id GetNumberOfValues() const { return this->Portal.GetNumberOfValues(); }
VTKM_EXEC_CONT
ValueType Get(vtkm::Id index) const
{
typename Traits::OutputType result;
Traits::Swizzle(this->Portal.Get(index), result);
return result;
}
VTKM_EXEC_CONT
void Set(vtkm::Id index, const ValueType& value) const
{
SetImpl<!Traits::ALL_COMPS_USED>(this->Portal)(index, value);
}
private:
// If NeedsRead is true, we need to initialize the InputType vector we write
// with the current values at @a index to avoid overwriting unused components.
template <bool NeedsRead>
struct SetImpl
{
const PortalType& Portal;
SetImpl(const PortalType& portal)
: Portal(portal)
{
}
void operator()(const vtkm::Id& index, const ValueType& value)
{
typename Traits::InputType in;
if (NeedsRead)
{
in = this->Portal.Get(index);
}
Traits::UnSwizzle(value, in);
this->Portal.Set(index, in);
}
};
public:
VTKM_EXEC_CONT
const PortalType& GetPortal() const { return this->Portal; }
private:
PortalType Portal;
}; // class ArrayPortalSwizzle
} // namespace internal
template <typename ArrayHandleType, vtkm::IdComponent... ComponentMap>
class StorageTagSwizzle
{
};
namespace internal
{
template <typename ArrayHandleType, vtkm::IdComponent... ComponentMap>
class Storage<typename ArrayHandleSwizzleTraits<typename ArrayHandleType::ValueType,
ComponentMap...>::OutputType,
StorageTagSwizzle<ArrayHandleType, ComponentMap...>>
{
public:
using PortalType = ArrayPortalSwizzle<typename ArrayHandleType::PortalControl, ComponentMap...>;
using PortalConstType =
ArrayPortalSwizzle<typename ArrayHandleType::PortalConstControl, ComponentMap...>;
using ValueType = typename PortalType::ValueType;
VTKM_CONT
Storage()
: Valid(false)
{
}
VTKM_CONT
Storage(const ArrayHandleType& array)
: Array(array)
, Valid(true)
{
}
VTKM_CONT
PortalConstType GetPortalConst() const
{
VTKM_ASSERT(this->Valid);
return PortalConstType(this->Array.GetPortalConstControl());
}
VTKM_CONT
PortalType GetPortal()
{
VTKM_ASSERT(this->Valid);
return PortalType(this->Array.GetPortalControl());
}
VTKM_CONT
vtkm::Id GetNumberOfValues() const
{
VTKM_ASSERT(this->Valid);
return this->Array.GetNumberOfValues();
}
VTKM_CONT
void Allocate(vtkm::Id numberOfValues)
{
VTKM_ASSERT(this->Valid);
this->Array.Allocate(numberOfValues);
}
VTKM_CONT
void Shrink(vtkm::Id numberOfValues)
{
VTKM_ASSERT(this->Valid);
this->Array.Shrink(numberOfValues);
}
VTKM_CONT
void ReleaseResources()
{
VTKM_ASSERT(this->Valid);
this->Array.ReleaseResources();
}
VTKM_CONT
const ArrayHandleType& GetArray() const
{
VTKM_ASSERT(this->Valid);
return this->Array;
}
private:
ArrayHandleType Array;
bool Valid;
}; // class Storage
template <typename ArrayHandleType, vtkm::IdComponent... ComponentMap, typename Device>
class ArrayTransfer<typename ArrayHandleSwizzleTraits<typename ArrayHandleType::ValueType,
ComponentMap...>::OutputType,
StorageTagSwizzle<ArrayHandleType, ComponentMap...>,
Device>
{
using ArrayExecutionTypes = typename ArrayHandleType::template ExecutionTypes<Device>;
using StorageTag = StorageTagSwizzle<ArrayHandleType, ComponentMap...>;
public:
using SwizzleTraits =
ArrayHandleSwizzleTraits<typename ArrayHandleType::ValueType, ComponentMap...>;
using ValueType = typename SwizzleTraits::OutputType;
private:
using StorageType = vtkm::cont::internal::Storage<ValueType, StorageTag>;
public:
using PortalControl = typename StorageType::PortalType;
using PortalConstControl = typename StorageType::PortalConstType;
using PortalExecution = ArrayPortalSwizzle<typename ArrayExecutionTypes::Portal, ComponentMap...>;
using PortalConstExecution =
ArrayPortalSwizzle<typename ArrayExecutionTypes::PortalConst, ComponentMap...>;
VTKM_CONT
ArrayTransfer(StorageType* storage)
: Array(storage->GetArray())
{
}
VTKM_CONT
vtkm::Id GetNumberOfValues() const { return this->Array.GetNumberOfValues(); }
VTKM_CONT
PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData))
{
return PortalConstExecution(this->Array.PrepareForInput(Device()));
}
VTKM_CONT
PortalExecution PrepareForInPlace(bool vtkmNotUsed(updateData))
{
return PortalExecution(this->Array.PrepareForInPlace(Device()));
}
VTKM_CONT
PortalExecution PrepareForOutput(vtkm::Id numberOfValues)
{
return PortalExecution(this->Array.PrepareForOutput(numberOfValues, Device()));
}
VTKM_CONT
void RetrieveOutputData(StorageType* vtkmNotUsed(storage)) const
{
// Implementation of this method should be unnecessary. The internal
// array handle should automatically retrieve the output data as
// necessary.
}
VTKM_CONT
void Shrink(vtkm::Id numberOfValues) { this->Array.Shrink(numberOfValues); }
VTKM_CONT
void ReleaseResources() { this->Array.ReleaseResourcesExecution(); }
private:
ArrayHandleType Array;
};
}
}
} // namespace vtkm::cont::internal
namespace vtkm
{
namespace cont
{
/// \brief A fancy ArrayHandle that rearranges and/or removes components of an
/// ArrayHandle with a vtkm::Vec ValueType.
///
/// ArrayHandleSwizzle is a specialization of ArrayHandle. It takes an
/// input ArrayHandle with a vtkm::Vec ValueType and a compile-time component
/// map and uses this information to create a new array consisting of the
/// specified components of the input ArrayHandle in the specified order. So for
/// a given index i, ArrayHandleSwizzle looks up the i-th vtkm::Vec in
/// the index array and reads or writes to the specified components, leaving all
/// other components unmodified. This is done on the fly rather than creating a
/// copy of the array.
template <typename ArrayHandleType, vtkm::IdComponent... ComponentMap>
class ArrayHandleSwizzle : public vtkm::cont::ArrayHandle<
typename ArrayHandleSwizzleTraits<typename ArrayHandleType::ValueType,
ComponentMap...>::OutputType,
StorageTagSwizzle<ArrayHandleType, ComponentMap...>>
{
public:
VTKM_ARRAY_HANDLE_SUBCLASS(
ArrayHandleSwizzle,
(ArrayHandleSwizzle<ArrayHandleType, ComponentMap...>),
(vtkm::cont::ArrayHandle<typename ArrayHandleSwizzleTraits<typename ArrayHandleType::ValueType,
ComponentMap...>::OutputType,
StorageTagSwizzle<ArrayHandleType, ComponentMap...>>));
using SwizzleTraits =
ArrayHandleSwizzleTraits<typename ArrayHandleType::ValueType, ComponentMap...>;
protected:
using StorageType = vtkm::cont::internal::Storage<ValueType, StorageTag>;
public:
VTKM_CONT
ArrayHandleSwizzle(const ArrayHandleType& array)
: Superclass(StorageType(array))
{
}
};
/// make_ArrayHandleSwizzle is convenience function to generate an
/// ArrayHandleSwizzle.
template <vtkm::IdComponent... ComponentMap, typename ArrayHandleType>
VTKM_CONT ArrayHandleSwizzle<ArrayHandleType, ComponentMap...> make_ArrayHandleSwizzle(
const ArrayHandleType& array)
{
return ArrayHandleSwizzle<ArrayHandleType, ComponentMap...>(array);
}
}
} // namespace vtkm::cont
#endif // vtk_m_cont_ArrayHandleSwizzle_h

@ -33,6 +33,7 @@ set(headers
ArrayHandlePermutation.h
ArrayHandleReverse.h
ArrayHandleStreaming.h
ArrayHandleSwizzle.h
ArrayHandleTransform.h
ArrayHandleUniformPointCoordinates.h
ArrayHandleZip.h

@ -43,6 +43,7 @@ set(unit_tests
UnitTestArrayHandleIndex.cxx
UnitTestArrayHandleReverse.cxx
UnitTestArrayHandlePermutation.cxx
UnitTestArrayHandleSwizzle.cxx
UnitTestArrayHandleTransform.cxx
UnitTestArrayHandleUniformPointCoordinates.cxx
UnitTestArrayHandleConcatenate.cxx

@ -0,0 +1,400 @@
//=============================================================================
//
// 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.
//
// Copyright 2015 Sandia Corporation.
// Copyright 2015 UT-Battelle, LLC.
// Copyright 2015 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//
//=============================================================================
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCompositeVector.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleSwizzle.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/testing/Testing.h>
#include <type_traits>
namespace
{
template <typename ValueType>
struct SwizzleTests
{
using SwizzleInputArrayType = vtkm::cont::ArrayHandle<vtkm::Vec<ValueType, 4>>;
template <vtkm::IdComponent... ComponentMap>
using SwizzleArrayType = vtkm::cont::ArrayHandleSwizzle<SwizzleInputArrayType, ComponentMap...>;
using ReferenceComponentArrayType = vtkm::cont::ArrayHandleCounting<ValueType>;
using ReferenceArrayType =
typename vtkm::cont::ArrayHandleCompositeVectorType<ReferenceComponentArrayType,
ReferenceComponentArrayType,
ReferenceComponentArrayType,
ReferenceComponentArrayType>::type;
using DeviceTag = VTKM_DEFAULT_DEVICE_ADAPTER_TAG;
using Algo = vtkm::cont::DeviceAdapterAlgorithm<DeviceTag>;
// This is used to build a ArrayHandleSwizzle's internal array.
ReferenceArrayType RefArray;
void ConstructReferenceArray()
{
// Build the Ref array
const vtkm::Id numValues = 32;
ReferenceComponentArrayType c1 =
vtkm::cont::make_ArrayHandleCounting<ValueType>(3, 2, numValues);
ReferenceComponentArrayType c2 =
vtkm::cont::make_ArrayHandleCounting<ValueType>(2, 3, numValues);
ReferenceComponentArrayType c3 =
vtkm::cont::make_ArrayHandleCounting<ValueType>(4, 4, numValues);
ReferenceComponentArrayType c4 =
vtkm::cont::make_ArrayHandleCounting<ValueType>(1, 3, numValues);
this->RefArray = vtkm::cont::make_ArrayHandleCompositeVector(c1, 0, c2, 0, c3, 0, c4, 0);
}
SwizzleInputArrayType BuildSwizzleInputArray() const
{
SwizzleInputArrayType result;
Algo::Copy(this->RefArray, result);
return result;
}
template <vtkm::IdComponent... ComponentMap>
void SanityCheck() const
{
using Swizzle = SwizzleArrayType<ComponentMap...>;
using Traits = typename Swizzle::SwizzleTraits;
VTKM_TEST_ASSERT(Traits::COUNT == vtkm::VecTraits<typename Swizzle::ValueType>::NUM_COMPONENTS,
"Traits::COUNT invalid.");
VTKM_TEST_ASSERT(
VTKM_PASS_COMMAS(std::is_same<typename Traits::ComponentType, ValueType>::value),
"Traits::ComponentType invalid.");
VTKM_TEST_ASSERT(
VTKM_PASS_COMMAS(
std::is_same<
typename Traits::OutputType,
vtkm::Vec<ValueType, static_cast<vtkm::IdComponent>(sizeof...(ComponentMap))>>::value),
"Traits::OutputType invalid.");
SwizzleInputArrayType input = this->BuildSwizzleInputArray();
SwizzleArrayType<ComponentMap...> swizzle =
vtkm::cont::make_ArrayHandleSwizzle<ComponentMap...>(input);
VTKM_TEST_ASSERT(input.GetNumberOfValues() == swizzle.GetNumberOfValues(),
"Number of values in copied Swizzle array does not match input.");
}
template <vtkm::IdComponent... ComponentMap>
void ReadTest() const
{
using Traits = typename SwizzleArrayType<ComponentMap...>::SwizzleTraits;
// Test that the expected values are read from an Swizzle array.
SwizzleInputArrayType input = this->BuildSwizzleInputArray();
SwizzleArrayType<ComponentMap...> swizzle =
vtkm::cont::make_ArrayHandleSwizzle<ComponentMap...>(input);
// Test reading the data back in the control env:
this->ValidateReadTest<ComponentMap...>(swizzle);
// Copy the extract array in the execution environment to test reading:
vtkm::cont::ArrayHandle<typename Traits::OutputType> execCopy;
Algo::Copy(swizzle, execCopy);
this->ValidateReadTest<ComponentMap...>(execCopy);
}
template <vtkm::IdComponent... ComponentMap, typename ArrayHandleType>
void ValidateReadTest(ArrayHandleType testArray) const
{
using Traits = typename SwizzleArrayType<ComponentMap...>::SwizzleTraits;
using MapType = typename Traits::RuntimeComponentMapType;
const MapType map = Traits::GenerateRuntimeComponentMap();
using ReferenceVectorType = typename ReferenceArrayType::ValueType;
using SwizzleVectorType = typename Traits::OutputType;
VTKM_TEST_ASSERT(map.size() == vtkm::VecTraits<SwizzleVectorType>::NUM_COMPONENTS,
"Unexpected runtime component map size.");
VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == this->RefArray.GetNumberOfValues(),
"Number of values incorrect in Read test.");
auto refPortal = this->RefArray.GetPortalConstControl();
auto testPortal = testArray.GetPortalConstControl();
for (vtkm::Id i = 0; i < testArray.GetNumberOfValues(); ++i)
{
// Manually swizzle the reference vector using the runtime map information:
ReferenceVectorType refVec = refPortal.Get(i);
SwizzleVectorType refVecSwizzle;
for (size_t j = 0; j < map.size(); ++j)
{
refVecSwizzle[static_cast<vtkm::IdComponent>(j)] = refVec[map[j]];
}
VTKM_TEST_ASSERT(test_equal(refVecSwizzle, testPortal.Get(i), 0.),
"Invalid value encountered in Read test.");
}
}
// Doubles everything in the input portal.
template <typename PortalType>
struct WriteTestFunctor : vtkm::exec::FunctorBase
{
PortalType Portal;
VTKM_CONT
WriteTestFunctor(const PortalType& portal)
: Portal(portal)
{
}
VTKM_EXEC_CONT
void operator()(vtkm::Id index) const { this->Portal.Set(index, this->Portal.Get(index) * 2.); }
};
template <vtkm::IdComponent... ComponentMap>
void WriteTest() const
{
// Control test:
{
SwizzleInputArrayType input = this->BuildSwizzleInputArray();
SwizzleArrayType<ComponentMap...> swizzle =
vtkm::cont::make_ArrayHandleSwizzle<ComponentMap...>(input);
WriteTestFunctor<typename SwizzleArrayType<ComponentMap...>::PortalControl> functor(
swizzle.GetPortalControl());
for (vtkm::Id i = 0; i < swizzle.GetNumberOfValues(); ++i)
{
functor(i);
}
this->ValidateWriteTestArray<ComponentMap...>(input);
}
// Exec test:
{
SwizzleInputArrayType input = this->BuildSwizzleInputArray();
SwizzleArrayType<ComponentMap...> swizzle =
vtkm::cont::make_ArrayHandleSwizzle<ComponentMap...>(input);
using Portal =
typename SwizzleArrayType<ComponentMap...>::template ExecutionTypes<DeviceTag>::Portal;
WriteTestFunctor<Portal> functor(swizzle.PrepareForInPlace(DeviceTag()));
Algo::Schedule(functor, swizzle.GetNumberOfValues());
this->ValidateWriteTestArray<ComponentMap...>(input);
}
}
// Check that the swizzled components are twice the reference value.
template <vtkm::IdComponent... ComponentMap>
void ValidateWriteTestArray(SwizzleInputArrayType testArray) const
{
using Traits = typename SwizzleArrayType<ComponentMap...>::SwizzleTraits;
using MapType = typename Traits::RuntimeComponentMapType;
const MapType map = Traits::GenerateRuntimeComponentMap();
auto refPortal = this->RefArray.GetPortalConstControl();
auto portal = testArray.GetPortalConstControl();
VTKM_TEST_ASSERT(portal.GetNumberOfValues() == refPortal.GetNumberOfValues(),
"Number of values in write test output do not match input.");
for (vtkm::Id i = 0; i < portal.GetNumberOfValues(); ++i)
{
auto value = portal.Get(i);
auto refValue = refPortal.Get(i);
// Double all of the components that appear in the map to replicate the
// test result:
for (size_t j = 0; j < map.size(); ++j)
{
refValue[map[j]] *= 2;
}
VTKM_TEST_ASSERT(test_equal(refValue, value, 0.), "Value mismatch in Write test.");
}
}
template <vtkm::IdComponent... ComponentMap>
void TestSwizzle() const
{
this->SanityCheck<ComponentMap...>();
this->ReadTest<ComponentMap...>();
this->WriteTest<ComponentMap...>();
}
void operator()()
{
this->ConstructReferenceArray();
// Enable for full test. We normally test a reduced set of component maps
// to keep compile times/sizes down:
#if 0
this->TestSwizzle<0, 1>();
this->TestSwizzle<0, 2>();
this->TestSwizzle<0, 3>();
this->TestSwizzle<1, 0>();
this->TestSwizzle<1, 2>();
this->TestSwizzle<1, 3>();
this->TestSwizzle<2, 0>();
this->TestSwizzle<2, 1>();
this->TestSwizzle<2, 3>();
this->TestSwizzle<3, 0>();
this->TestSwizzle<3, 1>();
this->TestSwizzle<3, 2>();
this->TestSwizzle<0, 1, 2>();
this->TestSwizzle<0, 1, 3>();
this->TestSwizzle<0, 2, 1>();
this->TestSwizzle<0, 2, 3>();
this->TestSwizzle<0, 3, 1>();
this->TestSwizzle<0, 3, 2>();
this->TestSwizzle<1, 0, 2>();
this->TestSwizzle<1, 0, 3>();
this->TestSwizzle<1, 2, 0>();
this->TestSwizzle<1, 2, 3>();
this->TestSwizzle<1, 3, 0>();
this->TestSwizzle<1, 3, 2>();
this->TestSwizzle<2, 0, 1>();
this->TestSwizzle<2, 0, 3>();
this->TestSwizzle<2, 1, 0>();
this->TestSwizzle<2, 1, 3>();
this->TestSwizzle<2, 3, 0>();
this->TestSwizzle<2, 3, 1>();
this->TestSwizzle<3, 0, 1>();
this->TestSwizzle<3, 0, 2>();
this->TestSwizzle<3, 1, 0>();
this->TestSwizzle<3, 1, 2>();
this->TestSwizzle<3, 2, 0>();
this->TestSwizzle<3, 2, 1>();
this->TestSwizzle<0, 1, 2, 3>();
this->TestSwizzle<0, 1, 3, 2>();
this->TestSwizzle<0, 2, 1, 3>();
this->TestSwizzle<0, 2, 3, 1>();
this->TestSwizzle<0, 3, 1, 2>();
this->TestSwizzle<0, 3, 2, 1>();
this->TestSwizzle<1, 0, 2, 3>();
this->TestSwizzle<1, 0, 3, 2>();
this->TestSwizzle<1, 2, 0, 3>();
this->TestSwizzle<1, 2, 3, 0>();
this->TestSwizzle<1, 3, 0, 2>();
this->TestSwizzle<1, 3, 2, 0>();
this->TestSwizzle<2, 0, 1, 3>();
this->TestSwizzle<2, 0, 3, 1>();
this->TestSwizzle<2, 1, 0, 3>();
this->TestSwizzle<2, 1, 3, 0>();
this->TestSwizzle<2, 3, 0, 1>();
this->TestSwizzle<2, 3, 1, 0>();
this->TestSwizzle<3, 0, 1, 2>();
this->TestSwizzle<3, 0, 2, 1>();
this->TestSwizzle<3, 1, 0, 2>();
this->TestSwizzle<3, 1, 2, 0>();
this->TestSwizzle<3, 2, 0, 1>();
this->TestSwizzle<3, 2, 1, 0>();
#else
this->TestSwizzle<0, 1>();
this->TestSwizzle<1, 0>();
this->TestSwizzle<2, 3>();
this->TestSwizzle<3, 2>();
this->TestSwizzle<0, 1, 2>();
this->TestSwizzle<0, 3, 1>();
this->TestSwizzle<2, 0, 3>();
this->TestSwizzle<3, 2, 1>();
this->TestSwizzle<0, 1, 2, 3>();
this->TestSwizzle<1, 3, 2, 0>();
this->TestSwizzle<2, 0, 1, 3>();
this->TestSwizzle<3, 1, 0, 2>();
this->TestSwizzle<3, 2, 1, 0>();
#endif
}
};
struct ArgToTemplateType
{
template <typename ValueType>
void operator()(ValueType) const
{
SwizzleTests<ValueType>()();
}
};
void TestArrayHandleSwizzle()
{
using TestTypes = vtkm::ListTagBase<vtkm::Int32, vtkm::Int64, vtkm::Float32, vtkm::Float64>;
vtkm::testing::Testing::TryTypes(ArgToTemplateType(), TestTypes());
}
template <vtkm::IdComponent InputSize, vtkm::IdComponent... ComponentMap>
using Validator = vtkm::cont::internal::ValidateComponentMap<InputSize, ComponentMap...>;
void TestComponentMapValidator()
{
using RepeatComps = Validator<5, 0, 3, 2, 3, 1, 4>;
VTKM_TEST_ASSERT(!RepeatComps::Valid, "Repeat components allowed.");
using NegativeComps = Validator<5, 0, 4, -3, 1, 2>;
VTKM_TEST_ASSERT(!NegativeComps::Valid, "Negative components allowed.");
using OutOfBoundsComps = Validator<5, 0, 2, 3, 5>;
VTKM_TEST_ASSERT(!OutOfBoundsComps::Valid, "Out-of-bounds components allowed.");
}
void TestRuntimeComponentMapGenerator()
{
// Dummy input vector type. Only concerned with the component map:
using Dummy = vtkm::Vec<char, 7>;
using Traits = vtkm::cont::ArrayHandleSwizzleTraits<Dummy, 3, 2, 4, 1, 6, 0>;
using MapType = Traits::RuntimeComponentMapType;
const MapType map = Traits::GenerateRuntimeComponentMap();
VTKM_TEST_ASSERT(map.size() == 6, "Invalid map size.");
VTKM_TEST_ASSERT(map[0] == 3, "Invalid map entry.");
VTKM_TEST_ASSERT(map[1] == 2, "Invalid map entry.");
VTKM_TEST_ASSERT(map[2] == 4, "Invalid map entry.");
VTKM_TEST_ASSERT(map[3] == 1, "Invalid map entry.");
VTKM_TEST_ASSERT(map[4] == 6, "Invalid map entry.");
VTKM_TEST_ASSERT(map[5] == 0, "Invalid map entry.");
}
} // end anon namespace
int UnitTestArrayHandleSwizzle(int, char* [])
{
try
{
TestComponentMapValidator();
TestRuntimeComponentMapGenerator();
}
catch (vtkm::cont::Error& e)
{
std::cerr << "Error: " << e.what() << "\n";
return EXIT_FAILURE;
}
return vtkm::cont::testing::Testing::Run(TestArrayHandleSwizzle);
}