mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-10-05 18:08:59 +00:00
a6eecbe9ac
Certain backends desire the ability to mark allocations as being used for reading versus writing to improve performance.
484 lines
16 KiB
C++
484 lines
16 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.
|
|
//
|
|
// Copyright 2017 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
|
|
// Copyright 2017 UT-Battelle, LLC.
|
|
// Copyright 2017 Los Alamos National Security.
|
|
//
|
|
// Under the terms of Contract DE-NA0003525 with NTESS,
|
|
// 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_internal_ArrayHandleBasicImpl_hxx
|
|
#define vtk_m_cont_internal_ArrayHandleBasicImpl_hxx
|
|
|
|
#include <vtkm/cont/internal/ArrayHandleBasicImpl.h>
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace cont
|
|
{
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>::ArrayHandle()
|
|
: Internals(new InternalStruct)
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const Thisclass& src)
|
|
: Internals(src.Internals)
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const Thisclass&& src)
|
|
: Internals(std::move(src.Internals))
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const StorageType& storage)
|
|
: Internals(new InternalStruct(storage))
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const std::shared_ptr<InternalStruct>& i)
|
|
: Internals(i)
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>::~ArrayHandle()
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>& ArrayHandle<T, StorageTagBasic>::operator=(const Thisclass& src)
|
|
{
|
|
this->Internals = src.Internals;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayHandle<T, StorageTagBasic>& ArrayHandle<T, StorageTagBasic>::operator=(Thisclass&& src)
|
|
{
|
|
this->Internals = std::move(src.Internals);
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
bool ArrayHandle<T, StorageTagBasic>::operator==(const Thisclass& rhs) const
|
|
{
|
|
return this->Internals == rhs.Internals;
|
|
}
|
|
|
|
template <typename T>
|
|
bool ArrayHandle<T, StorageTagBasic>::operator!=(const Thisclass& rhs) const
|
|
{
|
|
return this->Internals != rhs.Internals;
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename VT, typename ST>
|
|
VTKM_CONT bool ArrayHandle<T, StorageTagBasic>::operator==(const ArrayHandle<VT, ST>&) const
|
|
{
|
|
return false; // different valuetype and/or storage
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename VT, typename ST>
|
|
VTKM_CONT bool ArrayHandle<T, StorageTagBasic>::operator!=(const ArrayHandle<VT, ST>&) const
|
|
{
|
|
return true; // different valuetype and/or storage
|
|
}
|
|
|
|
template <typename T>
|
|
typename ArrayHandle<T, StorageTagBasic>::StorageType& ArrayHandle<T, StorageTagBasic>::GetStorage()
|
|
{
|
|
this->SyncControlArray();
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
return this->Internals->ControlArray;
|
|
}
|
|
else
|
|
{
|
|
throw vtkm::cont::ErrorInternal(
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
const typename ArrayHandle<T, StorageTagBasic>::StorageType&
|
|
ArrayHandle<T, StorageTagBasic>::GetStorage() const
|
|
{
|
|
this->SyncControlArray();
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
return this->Internals->ControlArray;
|
|
}
|
|
else
|
|
{
|
|
throw vtkm::cont::ErrorInternal(
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
typename ArrayHandle<T, StorageTagBasic>::PortalControl
|
|
ArrayHandle<T, StorageTagBasic>::GetPortalControl()
|
|
{
|
|
this->SyncControlArray();
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
// If the user writes into the iterator we return, then the execution
|
|
// array will become invalid. Play it safe and release the execution
|
|
// resources. (Use the const version to preserve the execution array.)
|
|
this->ReleaseResourcesExecutionInternal();
|
|
return this->Internals->ControlArray.GetPortal();
|
|
}
|
|
else
|
|
{
|
|
throw vtkm::cont::ErrorInternal(
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
}
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
typename ArrayHandle<T, StorageTagBasic>::PortalConstControl
|
|
ArrayHandle<T, StorageTagBasic>::GetPortalConstControl() const
|
|
{
|
|
this->SyncControlArray();
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
return this->Internals->ControlArray.GetPortalConst();
|
|
}
|
|
else
|
|
{
|
|
throw vtkm::cont::ErrorInternal(
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
vtkm::Id ArrayHandle<T, StorageTagBasic>::GetNumberOfValues() const
|
|
{
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
return this->Internals->ControlArray.GetNumberOfValues();
|
|
}
|
|
else if (this->Internals->ExecutionArrayValid)
|
|
{
|
|
return static_cast<vtkm::Id>(this->Internals->ExecutionArrayEnd -
|
|
this->Internals->ExecutionArray);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void ArrayHandle<T, StorageTagBasic>::Allocate(vtkm::Id numberOfValues)
|
|
{
|
|
this->ReleaseResourcesExecutionInternal();
|
|
this->Internals->ControlArray.Allocate(numberOfValues);
|
|
this->Internals->ControlArrayValid = true;
|
|
}
|
|
|
|
template <typename T>
|
|
void ArrayHandle<T, StorageTagBasic>::Shrink(vtkm::Id numberOfValues)
|
|
{
|
|
VTKM_ASSERT(numberOfValues >= 0);
|
|
|
|
if (numberOfValues > 0)
|
|
{
|
|
vtkm::Id originalNumberOfValues = this->GetNumberOfValues();
|
|
|
|
if (numberOfValues < originalNumberOfValues)
|
|
{
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
this->Internals->ControlArray.Shrink(numberOfValues);
|
|
}
|
|
if (this->Internals->ExecutionArrayValid)
|
|
{
|
|
this->Internals->ExecutionArrayEnd = this->Internals->ExecutionArray + numberOfValues;
|
|
}
|
|
}
|
|
else if (numberOfValues == originalNumberOfValues)
|
|
{
|
|
// Nothing to do.
|
|
}
|
|
else // numberOfValues > originalNumberOfValues
|
|
{
|
|
throw vtkm::cont::ErrorBadValue("ArrayHandle::Shrink cannot be used to grow array.");
|
|
}
|
|
|
|
VTKM_ASSERT(this->GetNumberOfValues() == numberOfValues);
|
|
}
|
|
else // numberOfValues == 0
|
|
{
|
|
// If we are shrinking to 0, there is nothing to save and we might as well
|
|
// free up memory. Plus, some storage classes expect that data will be
|
|
// deallocated when the size goes to zero.
|
|
this->Allocate(0);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void ArrayHandle<T, StorageTagBasic>::ReleaseResourcesExecution()
|
|
{
|
|
// Save any data in the execution environment by making sure it is synced
|
|
// with the control environment.
|
|
this->SyncControlArray();
|
|
this->ReleaseResourcesExecutionInternal();
|
|
}
|
|
|
|
template <typename T>
|
|
void ArrayHandle<T, StorageTagBasic>::ReleaseResources()
|
|
{
|
|
this->ReleaseResourcesExecutionInternal();
|
|
|
|
if (this->Internals->ControlArrayValid)
|
|
{
|
|
this->Internals->ControlArray.ReleaseResources();
|
|
this->Internals->ControlArrayValid = false;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename DeviceAdapterTag>
|
|
typename ArrayHandle<T, StorageTagBasic>::template ExecutionTypes<DeviceAdapterTag>::PortalConst
|
|
ArrayHandle<T, StorageTagBasic>::PrepareForInput(DeviceAdapterTag device) const
|
|
{
|
|
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
|
|
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
|
|
|
|
this->PrepareForDevice(device);
|
|
const vtkm::UInt64 numBytes = static_cast<vtkm::UInt64>(sizeof(ValueType)) *
|
|
static_cast<vtkm::UInt64>(this->GetStorage().GetNumberOfValues());
|
|
|
|
|
|
if (!this->Internals->ExecutionArrayValid)
|
|
{
|
|
// Initialize an empty array if needed:
|
|
if (!this->Internals->ControlArrayValid)
|
|
{
|
|
this->Internals->ControlArray.Allocate(0);
|
|
this->Internals->ControlArrayValid = true;
|
|
}
|
|
|
|
internal::TypelessExecutionArray execArray(
|
|
reinterpret_cast<void*&>(priv->ExecutionArray),
|
|
reinterpret_cast<void*&>(priv->ExecutionArrayEnd),
|
|
reinterpret_cast<void*&>(priv->ExecutionArrayCapacity),
|
|
this->Internals->ControlArray.GetBasePointer(),
|
|
this->Internals->ControlArray.GetCapacityPointer());
|
|
|
|
priv->ExecutionInterface->Allocate(execArray, numBytes);
|
|
|
|
priv->ExecutionInterface->CopyFromControl(
|
|
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
|
|
|
|
this->Internals->ExecutionArrayValid = true;
|
|
}
|
|
this->Internals->ExecutionInterface->UsingForRead(
|
|
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
|
|
|
|
return PortalFactory<DeviceAdapterTag>::CreatePortalConst(this->Internals->ExecutionArray,
|
|
this->Internals->ExecutionArrayEnd);
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename DeviceAdapterTag>
|
|
typename ArrayHandle<T, StorageTagBasic>::template ExecutionTypes<DeviceAdapterTag>::Portal
|
|
ArrayHandle<T, StorageTagBasic>::PrepareForOutput(vtkm::Id numVals, DeviceAdapterTag device)
|
|
{
|
|
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
|
|
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
|
|
|
|
this->PrepareForDevice(device);
|
|
|
|
// Invalidate control arrays since we expect the execution data to be
|
|
// overwritten. Don't free control resources in case they're shared with
|
|
// the execution environment.
|
|
this->Internals->ControlArrayValid = false;
|
|
|
|
internal::TypelessExecutionArray execArray(reinterpret_cast<void*&>(priv->ExecutionArray),
|
|
reinterpret_cast<void*&>(priv->ExecutionArrayEnd),
|
|
reinterpret_cast<void*&>(priv->ExecutionArrayCapacity),
|
|
this->Internals->ControlArray.GetBasePointer(),
|
|
this->Internals->ControlArray.GetCapacityPointer());
|
|
|
|
const vtkm::UInt64 numBytes =
|
|
static_cast<vtkm::UInt64>(sizeof(ValueType)) * static_cast<vtkm::UInt64>(numVals);
|
|
|
|
this->Internals->ExecutionInterface->Allocate(execArray, numBytes);
|
|
|
|
this->Internals->ExecutionInterface->UsingForWrite(
|
|
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
|
|
|
|
this->Internals->ExecutionArrayValid = true;
|
|
|
|
|
|
return PortalFactory<DeviceAdapterTag>::CreatePortal(this->Internals->ExecutionArray,
|
|
this->Internals->ExecutionArrayEnd);
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename DeviceAdapterTag>
|
|
typename ArrayHandle<T, StorageTagBasic>::template ExecutionTypes<DeviceAdapterTag>::Portal
|
|
ArrayHandle<T, StorageTagBasic>::PrepareForInPlace(DeviceAdapterTag device)
|
|
{
|
|
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
|
|
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
|
|
|
|
this->PrepareForDevice(device);
|
|
|
|
const vtkm::UInt64 numBytes = static_cast<vtkm::UInt64>(sizeof(ValueType)) *
|
|
static_cast<vtkm::UInt64>(this->GetStorage().GetNumberOfValues());
|
|
|
|
if (!this->Internals->ExecutionArrayValid)
|
|
{
|
|
// Initialize an empty array if needed:
|
|
if (!this->Internals->ControlArrayValid)
|
|
{
|
|
this->Internals->ControlArray.Allocate(0);
|
|
this->Internals->ControlArrayValid = true;
|
|
}
|
|
|
|
internal::TypelessExecutionArray execArray(
|
|
reinterpret_cast<void*&>(this->Internals->ExecutionArray),
|
|
reinterpret_cast<void*&>(this->Internals->ExecutionArrayEnd),
|
|
reinterpret_cast<void*&>(this->Internals->ExecutionArrayCapacity),
|
|
this->Internals->ControlArray.GetBasePointer(),
|
|
this->Internals->ControlArray.GetCapacityPointer());
|
|
|
|
priv->ExecutionInterface->Allocate(execArray, numBytes);
|
|
|
|
priv->ExecutionInterface->CopyFromControl(
|
|
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
|
|
|
|
this->Internals->ExecutionArrayValid = true;
|
|
}
|
|
|
|
priv->ExecutionInterface->UsingForReadWrite(
|
|
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
|
|
|
|
// Invalidate the control array, since we expect the values to be modified:
|
|
this->Internals->ControlArrayValid = false;
|
|
|
|
return PortalFactory<DeviceAdapterTag>::CreatePortal(this->Internals->ExecutionArray,
|
|
this->Internals->ExecutionArrayEnd);
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename DeviceAdapterTag>
|
|
void ArrayHandle<T, StorageTagBasic>::PrepareForDevice(DeviceAdapterTag) const
|
|
{
|
|
DeviceAdapterId devId = DeviceAdapterTraits<DeviceAdapterTag>::GetId();
|
|
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
|
|
|
|
// Check if the current device matches the last one and sync through
|
|
// the control environment if the device changes.
|
|
if (this->Internals->ExecutionInterface)
|
|
{
|
|
if (this->Internals->ExecutionInterface->GetDeviceId() == devId)
|
|
{
|
|
// All set, nothing to do.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Update the device allocator:
|
|
this->SyncControlArray();
|
|
internal::TypelessExecutionArray execArray(
|
|
reinterpret_cast<void*&>(priv->ExecutionArray),
|
|
reinterpret_cast<void*&>(priv->ExecutionArrayEnd),
|
|
reinterpret_cast<void*&>(priv->ExecutionArrayCapacity),
|
|
this->Internals->ControlArray.GetBasePointer(),
|
|
this->Internals->ControlArray.GetCapacityPointer());
|
|
priv->ExecutionInterface->Free(execArray);
|
|
delete priv->ExecutionInterface;
|
|
priv->ExecutionInterface = nullptr;
|
|
priv->ExecutionArrayValid = false;
|
|
}
|
|
}
|
|
|
|
VTKM_ASSERT(priv->ExecutionInterface == nullptr);
|
|
VTKM_ASSERT(!priv->ExecutionArrayValid);
|
|
|
|
priv->ExecutionInterface =
|
|
new internal::ExecutionArrayInterfaceBasic<DeviceAdapterTag>(this->Internals->ControlArray);
|
|
}
|
|
|
|
template <typename T>
|
|
DeviceAdapterId ArrayHandle<T, StorageTagBasic>::GetDeviceAdapterId() const
|
|
{
|
|
return this->Internals->ExecutionArrayValid ? this->Internals->ExecutionInterface->GetDeviceId()
|
|
: VTKM_DEVICE_ADAPTER_UNDEFINED;
|
|
}
|
|
|
|
template <typename T>
|
|
void ArrayHandle<T, StorageTagBasic>::SyncControlArray() const
|
|
{
|
|
if (!this->Internals->ControlArrayValid)
|
|
{
|
|
// Need to change some state that does not change the logical state from
|
|
// an external point of view.
|
|
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
|
|
if (this->Internals->ExecutionArrayValid)
|
|
{
|
|
const vtkm::Id numValues =
|
|
static_cast<vtkm::Id>(this->Internals->ExecutionArrayEnd - this->Internals->ExecutionArray);
|
|
const vtkm::UInt64 numBytes =
|
|
static_cast<vtkm::UInt64>(sizeof(ValueType)) * static_cast<vtkm::UInt64>(numValues);
|
|
priv->ControlArray.Allocate(numValues);
|
|
priv->ExecutionInterface->CopyToControl(
|
|
priv->ExecutionArray, priv->ControlArray.GetArray(), numBytes);
|
|
priv->ControlArrayValid = true;
|
|
}
|
|
else
|
|
{
|
|
// This array is in the null state (there is nothing allocated), but
|
|
// the calling function wants to do something with the array. Put this
|
|
// class into a valid state by allocating an array of size 0.
|
|
priv->ControlArray.Allocate(0);
|
|
priv->ControlArrayValid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void ArrayHandle<T, StorageTagBasic>::ReleaseResourcesExecutionInternal()
|
|
{
|
|
if (this->Internals->ExecutionArrayValid)
|
|
{
|
|
internal::TypelessExecutionArray execArray(
|
|
reinterpret_cast<void*&>(this->Internals->ExecutionArray),
|
|
reinterpret_cast<void*&>(this->Internals->ExecutionArrayEnd),
|
|
reinterpret_cast<void*&>(this->Internals->ExecutionArrayCapacity),
|
|
this->Internals->ControlArray.GetBasePointer(),
|
|
this->Internals->ControlArray.GetCapacityPointer());
|
|
this->Internals->ExecutionInterface->Free(execArray);
|
|
this->Internals->ExecutionArrayValid = false;
|
|
}
|
|
}
|
|
}
|
|
} // end namespace vtkm::cont
|
|
|
|
|
|
#endif // not vtk_m_cont_internal_ArrayHandleBasicImpl_hxx
|