Add standard support for read-only storage

Many of the fancy `ArrayHandle`s are read-only and therefore connot
really create write portals. Likewise, many `ArrayHandle`s (both read-
only and read/write) have no way to resize themselves. In this case,
implementing the `CreateWritePortal` and `ResizeBuffers` methods in the
`Storage` class was troublesome. Mostly they just threw an exception,
but they also sometimes had to deal with cases where the behavior was
allowed.

To simplify code for developers, this introduces a pair of macros:
`VTKM_STORAGE_NO_RESIZE` and `VTKM_STORAGE_NO_WRITE_PORTAL`. These can
be declared in a `Storage` implementation when the storage has no viable
way to resize itself and create a write portal, respectively.

Having boilerplate code for these methods also helps work around
expected behavior for `ResizeBuffers`. `ResizeBuffers` should silently
work when resizing to the same size. Also `ResizeBuffers` should behave
well when resizing to 0 as that is what `ReleaseResources` does.
This commit is contained in:
Kenneth Moreland 2020-12-07 15:19:24 -07:00
parent a6b9d5c497
commit 7811cc4b1e
9 changed files with 149 additions and 77 deletions

@ -221,6 +221,8 @@ class Storage<vtkm::Vec<T, 3>, vtkm::cont::StorageTagCartesianProduct<ST1, ST2,
}
public:
VTKM_STORAGE_NO_RESIZE;
using ReadPortalType =
vtkm::internal::ArrayPortalCartesianProduct<vtkm::Vec<T, 3>,
typename Storage1::ReadPortalType,
@ -245,22 +247,6 @@ public:
Storage3::GetNumberOfValues(Buffers3(buffers)));
}
VTKM_CONT static void ResizeBuffers(vtkm::Id numValues,
vtkm::cont::internal::Buffer* buffers,
vtkm::CopyFlag vtkmNotUsed(preserve),
vtkm::cont::Token& vtkmNotUsed(token))
{
if (numValues == GetNumberOfValues(buffers))
{
// In general, we don't allow resizing of the array, but if it was "allocated" to the
// correct size, we will allow that.
}
else
{
throw vtkm::cont::ErrorBadAllocation("Does not make sense.");
}
}
VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)

@ -97,11 +97,10 @@ struct VTKM_ALWAYS_EXPORT
{
VTKM_IS_TRIVIALLY_COPYABLE(ArrayPortalType);
using ReadPortalType = ArrayPortalType;
VTKM_STORAGE_NO_RESIZE;
VTKM_STORAGE_NO_WRITE_PORTAL;
// Note that this portal is almost certainly read-only, so you will probably get
// an error if you try to write to it.
using WritePortalType = ArrayPortalType;
using ReadPortalType = ArrayPortalType;
// Implicit array has one buffer that should be empty (NumberOfBytes = 0), but holds
// the metadata for the array.
@ -112,35 +111,12 @@ struct VTKM_ALWAYS_EXPORT
return buffers[0].GetMetaData<ArrayPortalType>().GetNumberOfValues();
}
VTKM_CONT static void ResizeBuffers(vtkm::Id numValues,
vtkm::cont::internal::Buffer* buffers,
vtkm::CopyFlag,
vtkm::cont::Token&)
{
if (numValues == GetNumberOfValues(buffers))
{
// In general, we don't allow resizing of the array, but if it was "allocated" to the
// correct size, we will allow that.
}
else
{
throw vtkm::cont::ErrorBadAllocation("Cannot allocate/resize implicit arrays.");
}
}
VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers,
vtkm::cont::DeviceAdapterId,
vtkm::cont::Token&)
{
return buffers[0].GetMetaData<ArrayPortalType>();
}
VTKM_CONT static WritePortalType CreateWritePortal(const vtkm::cont::internal::Buffer*,
vtkm::cont::DeviceAdapterId,
vtkm::cont::Token&)
{
throw vtkm::cont::ErrorBadAllocation("Cannot write to implicit arrays.");
}
};
/// Given an array portal, returns the buffers for the `ArrayHandle` with a storage that

@ -252,16 +252,14 @@ class Storage<typename StorageTagTransform<ArrayHandleType, FunctorType>::ValueT
static constexpr vtkm::IdComponent NUM_METADATA_BUFFERS = 1;
public:
VTKM_STORAGE_NO_RESIZE;
VTKM_STORAGE_NO_WRITE_PORTAL;
using ReadPortalType =
vtkm::internal::ArrayPortalTransform<ValueType,
typename ArrayHandleType::ReadPortalType,
typename FunctorManager::FunctorType>;
// Note that this array is read only, so you really should only be getting the const
// version of the portal. If you actually try to write to this portal, you will
// get an error.
using WritePortalType = ReadPortalType;
VTKM_CONT static vtkm::IdComponent GetNumberOfBuffers()
{
return SourceStorage::GetNumberOfBuffers() + NUM_METADATA_BUFFERS;
@ -272,23 +270,6 @@ public:
return SourceStorage::GetNumberOfValues(buffers + NUM_METADATA_BUFFERS);
}
VTKM_CONT static void ResizeBuffers(vtkm::Id numValues,
vtkm::cont::internal::Buffer* buffers,
vtkm::CopyFlag vtkmNotUsed(preserve),
vtkm::cont::Token& vtkmNotUsed(token))
{
if (numValues == GetNumberOfValues(buffers))
{
// In general, we don't allow resizing of the array, but if it was "allocated" to the
// correct size, we will allow that.
}
else
{
throw vtkm::cont::ErrorBadAllocation(
"ArrayHandleTransform is read only. It cannot be allocated.");
}
}
VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
@ -307,14 +288,6 @@ public:
}
}
VTKM_CONT static WritePortalType CreateWritePortal(vtkm::cont::internal::Buffer*,
vtkm::cont::DeviceAdapterId,
vtkm::cont::Token&)
{
throw vtkm::cont::ErrorBadType(
"ArrayHandleTransform is read only. Cannot get writable portal.");
}
VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> CreateBuffers(
const ArrayHandleType& handle,
const FunctorType& functor = FunctorType())

@ -151,6 +151,7 @@ set(sources
Initialize.cxx
Logging.cxx
RuntimeDeviceTracker.cxx
Storage.cxx
Token.cxx
TryExecute.cxx
)

45
vtkm/cont/Storage.cxx Normal file

@ -0,0 +1,45 @@
//============================================================================
// 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/Storage.h>
namespace vtkm
{
namespace cont
{
namespace internal
{
namespace detail
{
void StorageNoResizeImpl(vtkm::Id currentNumValues,
vtkm::Id requestedNumValues,
std::string storageTagName)
{
if (currentNumValues == requestedNumValues)
{
// Array resized to current size. This is OK.
}
else if (requestedNumValues == 0)
{
// Array resized to zero. This can happen when releasing resources.
// Should we try to clear out the buffers, or avoid that for messing up shared buffers?
}
else
{
throw vtkm::cont::ErrorBadAllocation("Cannot resize arrays with storage type of " +
storageTagName);
}
}
}
}
}
} // namespace vtkm::cont::internal::detail

@ -18,10 +18,16 @@
#define VTKM_STORAGE VTKM_STORAGE_BASIC
#endif
#include <vtkm/Flags.h>
#include <vtkm/StaticAssert.h>
#include <vtkm/cont/vtkm_cont_export.h>
#include <vtkm/internal/ExportMacros.h>
#include <vtkm/internal/ArrayPortalDummy.h>
#include <vtkm/cont/ErrorBadAllocation.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/Token.h>
#include <vtkm/cont/internal/Buffer.h>
namespace vtkm
{
@ -173,6 +179,48 @@ public:
};
#endif // VTKM_DOXYGEN_ONLY
namespace detail
{
VTKM_CONT_EXPORT void StorageNoResizeImpl(vtkm::Id currentNumValues,
vtkm::Id requestedNumValues,
std::string storageTagName);
} // namespace detail
template <typename StorageType>
struct StorageTraits;
template <typename T, typename S>
struct StorageTraits<vtkm::cont::internal::Storage<T, S>>
{
using ValueType = T;
using Tag = S;
};
#define VTKM_STORAGE_NO_RESIZE \
VTKM_CONT static void ResizeBuffers( \
vtkm::Id numValues, vtkm::cont::internal::Buffer* buffers, vtkm::CopyFlag, vtkm::cont::Token&) \
{ \
vtkm::cont::internal::detail::StorageNoResizeImpl( \
GetNumberOfValues(buffers), \
numValues, \
vtkm::cont::TypeToString<typename vtkm::cont::internal::StorageTraits<Storage>::Tag>()); \
} \
using ResizeBuffersEatComma = void
#define VTKM_STORAGE_NO_WRITE_PORTAL \
using WritePortalType = vtkm::internal::ArrayPortalDummy< \
typename vtkm::cont::internal::StorageTraits<Storage>::ValueType>; \
VTKM_CONT static WritePortalType CreateWritePortal( \
vtkm::cont::internal::Buffer*, vtkm::cont::DeviceAdapterId, vtkm::cont::Token&) \
{ \
throw vtkm::cont::ErrorBadAllocation( \
"Cannot write to arrays with storage type of " + \
vtkm::cont::TypeToString<typename vtkm::cont::internal::StorageTraits<Storage>::Tag>()); \
} \
using CreateWritePortalEatComma = void
} // namespace internal
}
} // namespace vtkm::cont

@ -343,8 +343,6 @@ private:
vtkm::cont::ArrayCopy(soaArray, basicArray);
VTKM_TEST_ASSERT(basicArray.GetNumberOfValues() == ARRAY_SIZE);
CheckPortal(basicArray.ReadPortal());
soaArray.ReleaseResources();
}
{
@ -373,6 +371,9 @@ private:
vtkm::cont::make_ArrayHandleSOA(vtkm::CopyFlag::Off, vector0, vector1, vector2);
VTKM_TEST_ASSERT(soaArray.GetNumberOfValues() == ARRAY_SIZE);
CheckPortal(soaArray.ReadPortal());
// Make sure calling ReleaseResources does not result in error.
soaArray.ReleaseResources();
}
{

@ -0,0 +1,41 @@
//============================================================================
// 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_internal_ArrayPortalDummy
#define vtk_m_internal_ArrayPortalDummy
#include <vtkm/Assert.h>
#include <vtkm/Types.h>
namespace vtkm
{
namespace internal
{
/// A class that can be used in place of an `ArrayPortal` when the `ArrayPortal` is
/// not actually supported. It allows templates to be compiled, but will cause undefined
/// behavior if actually used.
template <typename T>
struct ArrayPortalDummy
{
using ValueType = T;
VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return 0; }
VTKM_EXEC_CONT ValueType Get(vtkm::Id) const
{
VTKM_ASSERT(false && "Tried to use a dummy portal.");
return ValueType{};
}
};
}
} // namespace vtkm::internal
#endif //vtk_m_internal_ArrayPortalDummy

@ -49,6 +49,7 @@ vtkm_install_headers(vtkm/internal
set(headers
ArrayPortalBasic.h
ArrayPortalDummy.h
ArrayPortalHelpers.h
ArrayPortalUniformPointCoordinates.h
ArrayPortalValueReference.h