//============================================================================ // 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 2014 Sandia Corporation. // Copyright 2014 UT-Battelle, LLC. // Copyright 2014 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. //============================================================================ namespace vtkm { namespace cont { template ArrayHandle::ArrayHandle() : Internals(new InternalStruct) { this->Internals->ControlArrayValid = false; this->Internals->ExecutionArrayValid = false; } template ArrayHandle::ArrayHandle(const ArrayHandle& src) : Internals(src.Internals) { } template ArrayHandle::ArrayHandle(ArrayHandle&& src) : Internals(std::move(src.Internals)) { } template ArrayHandle::ArrayHandle(const typename ArrayHandle::StorageType& storage) : Internals(new InternalStruct) { this->Internals->ControlArray = storage; this->Internals->ControlArrayValid = true; this->Internals->ExecutionArrayValid = false; } template ArrayHandle::~ArrayHandle() { } template ArrayHandle& ArrayHandle::operator=(const ArrayHandle& src) { this->Internals = src.Internals; return *this; } template ArrayHandle& ArrayHandle::operator=(ArrayHandle&& src) { this->Internals = std::move(src.Internals); return *this; } template typename ArrayHandle::StorageType& ArrayHandle::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 const typename ArrayHandle::StorageType& ArrayHandle::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 ArrayHandle::PortalControl ArrayHandle::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 ArrayHandle::PortalConstControl ArrayHandle::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 vtkm::Id ArrayHandle::GetNumberOfValues() const { if (this->Internals->ControlArrayValid) { return this->Internals->ControlArray.GetNumberOfValues(); } else if (this->Internals->ExecutionArrayValid) { return this->Internals->ExecutionArray->GetNumberOfValues(); } else { return 0; } } template template void ArrayHandle::CopyInto(IteratorType dest, DeviceAdapterTag) const { using pointer_type = typename std::iterator_traits::pointer; using value_type = typename std::remove_pointer::type; static_assert(!std::is_const::value, "CopyInto requires a non const iterator."); VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag); if (!this->Internals->ControlArrayValid && !this->Internals->ExecutionArrayValid) { throw vtkm::cont::ErrorBadValue("ArrayHandle has no data to copy into Iterator."); } if (!this->Internals->ControlArrayValid && this->Internals->ExecutionArray->IsDeviceAdapter(DeviceAdapterTag())) { /// Dynamically cast ArrayHandleExecutionManagerBase into a concrete /// class and call CopyInto. The dynamic conversion will be sucessful /// becuase the check to ensure the ExecutionArray is of the type /// DeviceAdapterTag has already passed using ConcreteType = vtkm::cont::internal::ArrayHandleExecutionManager; ConcreteType* ConcreteExecutionArray = dynamic_cast(this->Internals->ExecutionArray.get()); ConcreteExecutionArray->CopyInto(dest); } else { PortalConstControl portal = this->GetPortalConstControl(); std::copy(vtkm::cont::ArrayPortalToIteratorBegin(portal), vtkm::cont::ArrayPortalToIteratorEnd(portal), dest); } } template void ArrayHandle::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->ExecutionArray->Shrink(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 template typename ArrayHandle::template ExecutionTypes::PortalConst ArrayHandle::PrepareForInput(DeviceAdapterTag) const { VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag); if (!this->Internals->ControlArrayValid && !this->Internals->ExecutionArrayValid) { // Want to use an empty array. // Set up ArrayHandle state so this actually works. this->Internals->ControlArray.Allocate(0); this->Internals->ControlArrayValid = true; } this->PrepareForDevice(DeviceAdapterTag()); typename ExecutionTypes::PortalConst portal = this->Internals->ExecutionArray->PrepareForInput(!this->Internals->ExecutionArrayValid, DeviceAdapterTag()); this->Internals->ExecutionArrayValid = true; return portal; } template template typename ArrayHandle::template ExecutionTypes::Portal ArrayHandle::PrepareForOutput(vtkm::Id numberOfValues, DeviceAdapterTag) { VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag); // Invalidate any control arrays. // Should the control array resource be released? Probably not a good // idea when shared with execution. this->Internals->ControlArrayValid = false; this->PrepareForDevice(DeviceAdapterTag()); typename ExecutionTypes::Portal portal = this->Internals->ExecutionArray->PrepareForOutput(numberOfValues, DeviceAdapterTag()); // We are assuming that the calling code will fill the array using the // iterators we are returning, so go ahead and mark the execution array as // having valid data. (A previous version of this class had a separate call // to mark the array as filled, but that was onerous to call at the the // right time and rather pointless since it is basically always the case // that the array is going to be filled before anything else. In this // implementation the only access to the array is through the iterators // returned from this method, so you would have to work to invalidate this // assumption anyway.) this->Internals->ExecutionArrayValid = true; return portal; } template template typename ArrayHandle::template ExecutionTypes::Portal ArrayHandle::PrepareForInPlace(DeviceAdapterTag) { VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag); if (!this->Internals->ControlArrayValid && !this->Internals->ExecutionArrayValid) { // Want to use an empty array. // Set up ArrayHandle state so this actually works. this->Internals->ControlArray.Allocate(0); this->Internals->ControlArrayValid = true; } this->PrepareForDevice(DeviceAdapterTag()); typename ExecutionTypes::Portal portal = this->Internals->ExecutionArray->PrepareForInPlace(!this->Internals->ExecutionArrayValid, DeviceAdapterTag()); this->Internals->ExecutionArrayValid = true; // Invalidate any control arrays since their data will become invalid when // the execution data is overwritten. Don't actually release the control // array. It may be shared as the execution array. this->Internals->ControlArrayValid = false; return portal; } template template void ArrayHandle::PrepareForDevice(DeviceAdapterTag) const { if (this->Internals->ExecutionArray != nullptr) { if (this->Internals->ExecutionArray->IsDeviceAdapter(DeviceAdapterTag())) { // Already have manager for correct device adapter. Nothing to do. return; } else { // Have the wrong manager. Delete the old one and create a new one // of the right type. (BTW, it would be possible for the array handle // to hold references to execution arrays on multiple devices. However, // there is not a clear use case for that yet and it is unclear what // the behavior of "dirty" arrays should be, so it is not currently // implemented.) this->SyncControlArray(); // Need to change some state that does not change the logical state from // an external point of view. InternalStruct* internals = const_cast(this->Internals.get()); internals->ExecutionArray.reset(); internals->ExecutionArrayValid = false; } } VTKM_ASSERT(this->Internals->ExecutionArray == nullptr); VTKM_ASSERT(!this->Internals->ExecutionArrayValid); // Need to change some state that does not change the logical state from // an external point of view. InternalStruct* internals = const_cast(this->Internals.get()); internals->ExecutionArray.reset( new vtkm::cont::internal::ArrayHandleExecutionManager( &internals->ControlArray)); } template void ArrayHandle::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* internals = const_cast(this->Internals.get()); if (this->Internals->ExecutionArrayValid) { internals->ExecutionArray->RetrieveOutputData(&internals->ControlArray); internals->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. internals->ControlArray.Allocate(0); internals->ControlArrayValid = true; } } } } }