mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-08 13:23:51 +00:00
Support using arrays with dynamic Vec-likes as output arrays
When you use an `ArrayHandle` as an output array in a worklet (for example, as a `FieldOut`), the fetch operation does not read values from the array during the `Load`. Instead, it just constructs a new object. This makes sense as an output array is expected to have garbage in it anyway. This is a problem for some special arrays that contain `Vec`-like objects that are sized dynamically. For example, if you use an `ArrayHandleGroupVecVariable`, each entry is a dynamically sized `Vec`. The array is referenced by creating a special version of `Vec` that holds a reference to the array portal and an index. Components are retrieved and set by accessing the memory in the array portal. This allows us to have a dynamically sized `Vec` in the execution environment without having to allocate within the worklet. The problem comes when we want to use one of these arrays with `Vec`-like objects for an output. The typical fetch fails because you cannot construct one of these `Vec`-like objects without an array portal to bind it to. In these cases, we need the fetch to create the `Vec`-like object by reading it from the array. Even though the data will be garbage, you get the necessary buffer into the array (and nothing more). Previously, the problem was fixed by creating partial specializations of the `Fetch` for these `ArrayHandle`s. This worked OK as long as you were using the array directly. However, the approach failed if the `ArrayHandle` was wrapped in another `ArrayHandle` (for example, if an `ArrayHandleView` was applied to an `ArrayHandleGroupVecVariable`). To get around this problem and simplify things, the basic `Fetch` for direct output arrays is changed to handle all cases where the values in the `ArrayHandle` cannot be directly constructed. A compile-time check of the array's value type is checked with `std::is_default_constructible`. If it can be constructed, then the array is not accessed. If it cannot be constructed, then it grabs a value out of the array.
This commit is contained in:
parent
679a61bb71
commit
eda6dc39f2
36
docs/changelog/output-vec-like-fix.md
Normal file
36
docs/changelog/output-vec-like-fix.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Support using arrays with dynamic Vec-likes as output arrays
|
||||||
|
|
||||||
|
When you use an `ArrayHandle` as an output array in a worklet (for example,
|
||||||
|
as a `FieldOut`), the fetch operation does not read values from the array
|
||||||
|
during the `Load`. Instead, it just constructs a new object. This makes
|
||||||
|
sense as an output array is expected to have garbage in it anyway.
|
||||||
|
|
||||||
|
This is a problem for some special arrays that contain `Vec`-like objects
|
||||||
|
that are sized dynamically. For example, if you use an
|
||||||
|
`ArrayHandleGroupVecVariable`, each entry is a dynamically sized `Vec`. The
|
||||||
|
array is referenced by creating a special version of `Vec` that holds a
|
||||||
|
reference to the array portal and an index. Components are retrieved and
|
||||||
|
set by accessing the memory in the array portal. This allows us to have a
|
||||||
|
dynamically sized `Vec` in the execution environment without having to
|
||||||
|
allocate within the worklet.
|
||||||
|
|
||||||
|
The problem comes when we want to use one of these arrays with `Vec`-like
|
||||||
|
objects for an output. The typical fetch fails because you cannot construct
|
||||||
|
one of these `Vec`-like objects without an array portal to bind it to. In
|
||||||
|
these cases, we need the fetch to create the `Vec`-like object by reading
|
||||||
|
it from the array. Even though the data will be garbage, you get the
|
||||||
|
necessary buffer into the array (and nothing more).
|
||||||
|
|
||||||
|
Previously, the problem was fixed by creating partial specializations of
|
||||||
|
the `Fetch` for these `ArrayHandle`s. This worked OK as long as you were
|
||||||
|
using the array directly. However, the approach failed if the `ArrayHandle`
|
||||||
|
was wrapped in another `ArrayHandle` (for example, if an `ArrayHandleView`
|
||||||
|
was applied to an `ArrayHandleGroupVecVariable`).
|
||||||
|
|
||||||
|
To get around this problem and simplify things, the basic `Fetch` for
|
||||||
|
direct output arrays is changed to handle all cases where the values in the
|
||||||
|
`ArrayHandle` cannot be directly constructed. A compile-time check of the
|
||||||
|
array's value type is checked with `std::is_default_constructible`. If it
|
||||||
|
can be constructed, then the array is not accessed. If it cannot be
|
||||||
|
constructed, then it grabs a value out of the array.
|
||||||
|
|
@ -31,14 +31,6 @@ class VecFromPortal
|
|||||||
public:
|
public:
|
||||||
using ComponentType = typename std::remove_const<typename PortalType::ValueType>::type;
|
using ComponentType = typename std::remove_const<typename PortalType::ValueType>::type;
|
||||||
|
|
||||||
VTKM_SUPPRESS_EXEC_WARNINGS
|
|
||||||
VTKM_EXEC_CONT
|
|
||||||
VecFromPortal()
|
|
||||||
: NumComponents(0)
|
|
||||||
, Offset(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VTKM_SUPPRESS_EXEC_WARNINGS
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
||||||
VTKM_EXEC_CONT
|
VTKM_EXEC_CONT
|
||||||
VecFromPortal(const PortalType& portal, vtkm::IdComponent numComponents = 0, vtkm::Id offset = 0)
|
VecFromPortal(const PortalType& portal, vtkm::IdComponent numComponents = 0, vtkm::Id offset = 0)
|
||||||
|
@ -314,10 +314,6 @@ make_ArrayHandleGroupVecVariable(const ComponentsArrayHandleType& componentsArra
|
|||||||
}
|
}
|
||||||
} // namespace vtkm::cont
|
} // namespace vtkm::cont
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
// Specializations of worklet arguments using ArrayHandleGropuVecVariable
|
|
||||||
#include <vtkm/exec/arg/FetchTagArrayDirectOutArrayHandleGroupVecVariable.h>
|
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Specializations of serialization related classes
|
// Specializations of serialization related classes
|
||||||
/// @cond SERIALIZATION
|
/// @cond SERIALIZATION
|
||||||
|
@ -78,7 +78,14 @@ public:
|
|||||||
|
|
||||||
VTKM_EXEC_CONT RecombineVec& operator=(const RecombineVec& src)
|
VTKM_EXEC_CONT RecombineVec& operator=(const RecombineVec& src)
|
||||||
{
|
{
|
||||||
this->DoCopy(src);
|
if ((&this->Portals[0] != &src.Portals[0]) || (this->Index != src.Index))
|
||||||
|
{
|
||||||
|
this->DoCopy(src);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Copying to myself. Do not need to do anything.
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,8 +655,4 @@ struct ArrayExtractComponentImpl<vtkm::cont::internal::StorageTagRecombineVec>
|
|||||||
}
|
}
|
||||||
} // namespace vtkm::cont
|
} // namespace vtkm::cont
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
// Specializations of worklet arguments using ArrayHandleGropuVecVariable
|
|
||||||
#include <vtkm/exec/arg/FetchTagArrayDirectOutArrayHandleRecombineVec.h>
|
|
||||||
|
|
||||||
#endif //vtk_m_cont_ArrayHandleRecombineVec_h
|
#endif //vtk_m_cont_ArrayHandleRecombineVec_h
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <vtkm/cont/ArrayHandleRecombineVec.h>
|
#include <vtkm/cont/ArrayHandleRecombineVec.h>
|
||||||
|
|
||||||
|
#include <vtkm/cont/ArrayHandleReverse.h>
|
||||||
#include <vtkm/cont/Invoker.h>
|
#include <vtkm/cont/Invoker.h>
|
||||||
|
|
||||||
#include <vtkm/worklet/WorkletMapField.h>
|
#include <vtkm/worklet/WorkletMapField.h>
|
||||||
@ -80,8 +81,12 @@ struct TestRecombineVecAsOutput
|
|||||||
|
|
||||||
vtkm::cont::Invoker invoke;
|
vtkm::cont::Invoker invoke;
|
||||||
invoke(PassThrough{}, baseArray, recombinedArray);
|
invoke(PassThrough{}, baseArray, recombinedArray);
|
||||||
|
|
||||||
VTKM_TEST_ASSERT(test_equal_ArrayHandles(baseArray, outputArray));
|
VTKM_TEST_ASSERT(test_equal_ArrayHandles(baseArray, outputArray));
|
||||||
|
|
||||||
|
// Try outputing to a recombine vec inside of another fancy ArrayHandle.
|
||||||
|
auto reverseOutput = vtkm::cont::make_ArrayHandleReverse(recombinedArray);
|
||||||
|
invoke(PassThrough{}, baseArray, reverseOutput);
|
||||||
|
VTKM_TEST_ASSERT(test_equal_ArrayHandles(baseArray, reverseOutput));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,8 +19,6 @@ set(headers
|
|||||||
FetchTagArrayDirectIn.h
|
FetchTagArrayDirectIn.h
|
||||||
FetchTagArrayDirectInOut.h
|
FetchTagArrayDirectInOut.h
|
||||||
FetchTagArrayDirectOut.h
|
FetchTagArrayDirectOut.h
|
||||||
FetchTagArrayDirectOutArrayHandleGroupVecVariable.h
|
|
||||||
FetchTagArrayDirectOutArrayHandleRecombineVec.h
|
|
||||||
FetchTagArrayNeighborhoodIn.h
|
FetchTagArrayNeighborhoodIn.h
|
||||||
FetchTagArrayTopologyMapIn.h
|
FetchTagArrayTopologyMapIn.h
|
||||||
FetchTagExecObject.h
|
FetchTagExecObject.h
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#include <vtkm/exec/arg/AspectTagDefault.h>
|
#include <vtkm/exec/arg/AspectTagDefault.h>
|
||||||
#include <vtkm/exec/arg/Fetch.h>
|
#include <vtkm/exec/arg/Fetch.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace vtkm
|
namespace vtkm
|
||||||
{
|
{
|
||||||
namespace exec
|
namespace exec
|
||||||
@ -35,14 +37,15 @@ struct Fetch<vtkm::exec::arg::FetchTagArrayDirectOut,
|
|||||||
vtkm::exec::arg::AspectTagDefault,
|
vtkm::exec::arg::AspectTagDefault,
|
||||||
ExecObjectType>
|
ExecObjectType>
|
||||||
{
|
{
|
||||||
|
using ValueType = typename ExecObjectType::ValueType;
|
||||||
|
|
||||||
VTKM_SUPPRESS_EXEC_WARNINGS
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
||||||
template <typename ThreadIndicesType>
|
template <typename ThreadIndicesType>
|
||||||
VTKM_EXEC auto Load(const ThreadIndicesType&, const ExecObjectType&) const ->
|
VTKM_EXEC ValueType Load(const ThreadIndicesType& indices,
|
||||||
typename ExecObjectType::ValueType
|
const ExecObjectType& arrayPortal) const
|
||||||
{
|
{
|
||||||
// Load is a no-op for this fetch.
|
return this->DoLoad(
|
||||||
using ValueType = typename ExecObjectType::ValueType;
|
indices, arrayPortal, typename std::is_default_constructible<ValueType>::type{});
|
||||||
return ValueType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VTKM_SUPPRESS_EXEC_WARNINGS
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
||||||
@ -51,9 +54,32 @@ struct Fetch<vtkm::exec::arg::FetchTagArrayDirectOut,
|
|||||||
const ExecObjectType& arrayPortal,
|
const ExecObjectType& arrayPortal,
|
||||||
const T& value) const
|
const T& value) const
|
||||||
{
|
{
|
||||||
using ValueType = typename ExecObjectType::ValueType;
|
|
||||||
arrayPortal.Set(indices.GetOutputIndex(), static_cast<ValueType>(value));
|
arrayPortal.Set(indices.GetOutputIndex(), static_cast<ValueType>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
||||||
|
template <typename ThreadIndicesType>
|
||||||
|
VTKM_EXEC ValueType DoLoad(const ThreadIndicesType&, const ExecObjectType&, std::true_type) const
|
||||||
|
{
|
||||||
|
// Load is a no-op for this fetch.
|
||||||
|
return ValueType();
|
||||||
|
}
|
||||||
|
|
||||||
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
||||||
|
template <typename ThreadIndicesType>
|
||||||
|
VTKM_EXEC ValueType DoLoad(const ThreadIndicesType& indices,
|
||||||
|
const ExecObjectType& arrayPortal,
|
||||||
|
std::false_type) const
|
||||||
|
{
|
||||||
|
// Cannot create a ValueType object, so pull one out of the array portal. This may seem
|
||||||
|
// weird because an output array often has garbage in it. However, this case can happen
|
||||||
|
// with special arrays with Vec-like values that reference back to the array memory.
|
||||||
|
// For example, with ArrayHandleRecombineVec, the values are actual objects that point
|
||||||
|
// back to the array for on demand reading and writing. You need the buffer established
|
||||||
|
// by the array even if there is garbage in that array.
|
||||||
|
return arrayPortal.Get(indices.GetOutputIndex());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
//============================================================================
|
|
||||||
// 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_exec_arg_FetchTagArrayDirectOutArrayHandleGroupVecVariable_h
|
|
||||||
#define vtk_m_exec_arg_FetchTagArrayDirectOutArrayHandleGroupVecVariable_h
|
|
||||||
|
|
||||||
#include <vtkm/exec/arg/FetchTagArrayDirectOut.h>
|
|
||||||
|
|
||||||
// We need to override the fetch for output fields using
|
|
||||||
// ArrayPortalGroupVecVariable because this portal does not behave like most
|
|
||||||
// ArrayPortals. Usually you ignore the Load and implement the Store. But if
|
|
||||||
// you ignore the Load, the VecFromPortal gets no portal to set values into.
|
|
||||||
// Instead, you need to implement the Load to point to the array portal. You
|
|
||||||
// can also ignore the Store because the data is already set in the array at
|
|
||||||
// that point.
|
|
||||||
|
|
||||||
// This file is included from ArrayHandleGroupVecVariable.h
|
|
||||||
|
|
||||||
namespace vtkm
|
|
||||||
{
|
|
||||||
namespace exec
|
|
||||||
{
|
|
||||||
namespace arg
|
|
||||||
{
|
|
||||||
|
|
||||||
// We need to override the fetch for output fields using
|
|
||||||
// ArrayPortalGroupVecVariable because this portal does not behave like most
|
|
||||||
// ArrayPortals. Usually you ignore the Load and implement the Store. But if
|
|
||||||
// you ignore the Load, the VecFromPortal gets no portal to set values into.
|
|
||||||
// Instead, you need to implement the Load to point to the array portal. You
|
|
||||||
// can also ignore the Store because the data is already set in the array at
|
|
||||||
// that point.
|
|
||||||
template <typename ComponentsPortalType, typename OffsetsPortalType>
|
|
||||||
struct Fetch<vtkm::exec::arg::FetchTagArrayDirectOut,
|
|
||||||
vtkm::exec::arg::AspectTagDefault,
|
|
||||||
vtkm::internal::ArrayPortalGroupVecVariable<ComponentsPortalType, OffsetsPortalType>>
|
|
||||||
{
|
|
||||||
using ExecObjectType =
|
|
||||||
vtkm::internal::ArrayPortalGroupVecVariable<ComponentsPortalType, OffsetsPortalType>;
|
|
||||||
using ValueType = typename ExecObjectType::ValueType;
|
|
||||||
|
|
||||||
VTKM_SUPPRESS_EXEC_WARNINGS
|
|
||||||
template <typename ThreadIndicesType>
|
|
||||||
VTKM_EXEC ValueType Load(const ThreadIndicesType& indices,
|
|
||||||
const ExecObjectType& arrayPortal) const
|
|
||||||
{
|
|
||||||
return arrayPortal.Get(indices.GetOutputIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
VTKM_SUPPRESS_EXEC_WARNINGS
|
|
||||||
template <typename ThreadIndicesType>
|
|
||||||
VTKM_EXEC void Store(const ThreadIndicesType&, const ExecObjectType&, const ValueType&) const
|
|
||||||
{
|
|
||||||
// We can actually ignore this because the VecFromPortal will already have
|
|
||||||
// set new values in the array.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace vtkm::exec::arg
|
|
||||||
|
|
||||||
#endif //vtk_m_exec_arg_FetchTagArrayDirectOutArrayHandleGroupVecVariable_h
|
|
@ -1,46 +0,0 @@
|
|||||||
//============================================================================
|
|
||||||
// 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_exec_arg_FetchTagArrayDirectOutArrayHandleRecombineVec_h
|
|
||||||
#define vtk_m_exec_arg_FetchTagArrayDirectOutArrayHandleRecombineVec_h
|
|
||||||
|
|
||||||
#include <vtkm/exec/arg/FetchTagArrayDirectInOut.h>
|
|
||||||
#include <vtkm/exec/arg/FetchTagArrayDirectOut.h>
|
|
||||||
|
|
||||||
// The `Fetch` for direct array out breaks for `ArrayHandleRecombineVec` because the `Load`
|
|
||||||
// method attempts to create a `vtkm::internal::RecombineVec` with a default constructor,
|
|
||||||
// which does not exist. Instead, have the direct out `Fetch` behave like the direct in/out
|
|
||||||
// `Fetch`, which loads the initial value from the array. The actual load will not load the
|
|
||||||
// data but rather set up the portals in the returned object, which is necessary for the
|
|
||||||
// later `Store` to work anyway.
|
|
||||||
|
|
||||||
// This file is included from ArrayHandleRecombineVec.h
|
|
||||||
|
|
||||||
namespace vtkm
|
|
||||||
{
|
|
||||||
namespace exec
|
|
||||||
{
|
|
||||||
namespace arg
|
|
||||||
{
|
|
||||||
|
|
||||||
template <typename SourcePortalType>
|
|
||||||
struct Fetch<vtkm::exec::arg::FetchTagArrayDirectOut,
|
|
||||||
vtkm::exec::arg::AspectTagDefault,
|
|
||||||
vtkm::internal::ArrayPortalRecombineVec<SourcePortalType>>
|
|
||||||
: Fetch<vtkm::exec::arg::FetchTagArrayDirectInOut,
|
|
||||||
vtkm::exec::arg::AspectTagDefault,
|
|
||||||
vtkm::internal::ArrayPortalRecombineVec<SourcePortalType>>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace vtkm::exec::arg
|
|
||||||
|
|
||||||
#endif //vtk_m_exec_arg_FetchTagArrayDirectOutArrayHandleRecombineVec_h
|
|
Loading…
Reference in New Issue
Block a user