From 4345fe26b0951187f915e02f4a6a8f596455f39a Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 24 Aug 2020 15:26:57 -0600 Subject: [PATCH] Store the number of bits of a BitField in the Buffer's metadata The number of bits in a `BitField` cannot be directly implied from the size of the buffer (because the buffer gets padded to the nearest sized word). Thus, the `BitField stored the number of bits in its own internals. Unfortunately, that caused issues when passing the `BitField` data between it and an `ArrayHandleBitField`. If the `ArrayHandleBitField` resized itself, the `BitField` would not see the new size because it ignored the new buffer size. To get around this problem, `BitField` now declares its own `BufferMetaData` that stores the number of bits. Now, since the number of bits is stored in the `Buffer` object, it is sufficient to just share the `Buffer` to synchronize all of the state. --- vtkm/cont/ArrayHandle.cxx | 4 +- vtkm/cont/ArrayHandleBitField.h | 42 ++-- vtkm/cont/BitField.cxx | 187 +++++++++++++++ vtkm/cont/BitField.h | 212 ++++++------------ vtkm/cont/CMakeLists.txt | 1 + vtkm/cont/internal/Buffer.cxx | 13 +- vtkm/cont/internal/Buffer.h | 16 +- .../internal/DeviceAdapterAlgorithmGeneral.h | 4 +- vtkm/cont/testing/TestingBitField.h | 3 +- 9 files changed, 309 insertions(+), 173 deletions(-) create mode 100644 vtkm/cont/BitField.cxx diff --git a/vtkm/cont/ArrayHandle.cxx b/vtkm/cont/ArrayHandle.cxx index 187a2da65..3ecbc7776 100644 --- a/vtkm/cont/ArrayHandle.cxx +++ b/vtkm/cont/ArrayHandle.cxx @@ -24,9 +24,7 @@ VTKM_CONT void ArrayHandleReleaseResourcesExecution( for (auto&& buf : buffers) { - // Getting a write host buffer should invalidate any execution arrays. - // Might want to make something more explicit in Buffer. - buf.WritePointerHost(token); + buf.ReleaseDeviceResources(); } } diff --git a/vtkm/cont/ArrayHandleBitField.h b/vtkm/cont/ArrayHandleBitField.h index cfe213b1b..6cc445f3a 100644 --- a/vtkm/cont/ArrayHandleBitField.h +++ b/vtkm/cont/ArrayHandleBitField.h @@ -74,8 +74,6 @@ class Storage using BitPortalType = vtkm::cont::detail::BitPortal; using BitPortalConstType = vtkm::cont::detail::BitPortalConst; - vtkm::Id NumberOfBits; - using WordType = vtkm::WordTypeDefault; static constexpr vtkm::Id BlockSize = vtkm::cont::detail::BitFieldTraits::BlockSize; VTKM_STATIC_ASSERT(BlockSize >= static_cast(sizeof(WordType))); @@ -86,55 +84,53 @@ public: VTKM_CONT vtkm::IdComponent GetNumberOfBuffers() const { return 1; } - VTKM_CONT Storage() - : NumberOfBits(0) - { - } + VTKM_CONT Storage() {} - explicit VTKM_CONT Storage(vtkm::Id numberOfBits) - : NumberOfBits(numberOfBits) - { - } - - VTKM_CONT void ResizeBuffers(vtkm::Id numValues, + VTKM_CONT void ResizeBuffers(vtkm::Id numberOfBits, vtkm::cont::internal::Buffer* buffers, vtkm::CopyFlag preserve, vtkm::cont::Token& token) { - this->NumberOfBits = numValues; - - const vtkm::Id bytesNeeded = (this->NumberOfBits + CHAR_BIT - 1) / CHAR_BIT; + const vtkm::Id bytesNeeded = (numberOfBits + CHAR_BIT - 1) / CHAR_BIT; const vtkm::Id blocksNeeded = (bytesNeeded + BlockSize - 1) / BlockSize; const vtkm::Id numBytes = blocksNeeded * BlockSize; + VTKM_LOG_F(vtkm::cont::LogLevel::MemCont, + "BitField Allocation: %llu bits, blocked up to %s bytes.", + static_cast(numberOfBits), + vtkm::cont::GetSizeString(static_cast(numBytes)).c_str()); + buffers[0].SetNumberOfBytes(numBytes, preserve, token); + vtkm::cont::detail::GetBitFieldMetaData(buffers[0])->NumberOfBits = numberOfBits; } VTKM_CONT vtkm::Id GetNumberOfValues(const vtkm::cont::internal::Buffer* buffers) { - VTKM_ASSERT((buffers[0].GetNumberOfBytes() * CHAR_BIT) >= this->NumberOfBits); - (void)buffers; - return this->NumberOfBits; + vtkm::Id numberOfBits = vtkm::cont::detail::GetBitFieldMetaData(buffers[0])->NumberOfBits; + VTKM_ASSERT((buffers[0].GetNumberOfBytes() * CHAR_BIT) >= numberOfBits); + return numberOfBits; } VTKM_CONT ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { - VTKM_ASSERT((buffers[0].GetNumberOfBytes() * CHAR_BIT) >= this->NumberOfBits); + vtkm::Id numberOfBits = this->GetNumberOfValues(buffers); + VTKM_ASSERT((buffers[0].GetNumberOfBytes() * CHAR_BIT) >= numberOfBits); return ReadPortalType( - BitPortalConstType(buffers[0].ReadPointerDevice(device, token), this->NumberOfBits)); + BitPortalConstType(buffers[0].ReadPointerDevice(device, token), numberOfBits)); } VTKM_CONT WritePortalType CreateWritePortal(const vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { - VTKM_ASSERT((buffers[0].GetNumberOfBytes() * CHAR_BIT) >= this->NumberOfBits); + vtkm::Id numberOfBits = this->GetNumberOfValues(buffers); + VTKM_ASSERT((buffers[0].GetNumberOfBytes() * CHAR_BIT) >= numberOfBits); return WritePortalType( - BitPortalType(buffers[0].WritePointerDevice(device, token), this->NumberOfBits)); + BitPortalType(buffers[0].WritePointerDevice(device, token), numberOfBits)); } }; @@ -223,7 +219,7 @@ public: VTKM_CONT explicit ArrayHandleBitField(const vtkm::cont::BitField& bitField) - : Superclass(bitField.GetData().GetBuffers(), StorageType(bitField.GetNumberOfBits())) + : Superclass(std::vector(1, bitField.GetBuffer())) { } }; diff --git a/vtkm/cont/BitField.cxx b/vtkm/cont/BitField.cxx new file mode 100644 index 000000000..399b544c0 --- /dev/null +++ b/vtkm/cont/BitField.cxx @@ -0,0 +1,187 @@ +//============================================================================ +// 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 +#include + +namespace +{ + +struct DeviceCheckFunctor +{ + vtkm::cont::DeviceAdapterId FoundDevice = vtkm::cont::DeviceAdapterTagUndefined{}; + + VTKM_CONT void operator()(vtkm::cont::DeviceAdapterId device, + const vtkm::cont::internal::Buffer& buffer) + { + if (this->FoundDevice == vtkm::cont::DeviceAdapterTagUndefined{}) + { + if (buffer.IsAllocatedOnDevice(device)) + { + this->FoundDevice = device; + } + } + } +}; + +} // anonymous namespace + +namespace vtkm +{ +namespace cont +{ + +namespace detail +{ + +vtkm::cont::internal::BitFieldMetaData* GetBitFieldMetaData( + const vtkm::cont::internal::Buffer& buffer) +{ + vtkm::cont::internal::BufferMetaData* generalMetaData = buffer.GetMetaData(); + if (generalMetaData == nullptr) + { + VTKM_LOG_F(vtkm::cont::LogLevel::Warn, "BitField has buffer with no metadata. Setting."); + const_cast(buffer).SetMetaData( + vtkm::cont::internal::BitFieldMetaData{}); + generalMetaData = buffer.GetMetaData(); + VTKM_ASSERT(generalMetaData != nullptr); + } + + vtkm::cont::internal::BitFieldMetaData* metadata = + dynamic_cast(generalMetaData); + if (metadata == nullptr) + { + VTKM_LOG_F(vtkm::cont::LogLevel::Error, + "BitField has a buffer with metadata of the wrong type. " + "Replacing, but this will likely cause problems."); + const_cast(buffer).SetMetaData( + vtkm::cont::internal::BitFieldMetaData{}); + generalMetaData = buffer.GetMetaData(); + metadata = dynamic_cast(generalMetaData); + VTKM_ASSERT(metadata != nullptr); + } + + // Check to make sure that the buffer is at least as larger as needed for buffer size. + VTKM_ASSERT(buffer.GetNumberOfBytes() * CHAR_BIT >= metadata->NumberOfBits); + + return metadata; +} + +} // namespace detail + +namespace internal +{ + +BitFieldMetaData::~BitFieldMetaData() {} + +std::unique_ptr BitFieldMetaData::DeepCopy() const +{ + return std::unique_ptr(new BitFieldMetaData(*this)); +} + +} // namespace internal + +BitField::BitField() +{ + this->Buffer.SetMetaData(internal::BitFieldMetaData{}); +} + +vtkm::Id BitField::GetNumberOfBits() const +{ + auto metadata = detail::GetBitFieldMetaData(this->Buffer); + return metadata->NumberOfBits; +} + +void BitField::Allocate(vtkm::Id numberOfBits, + vtkm::CopyFlag preserve, + vtkm::cont::Token& token) const +{ + const vtkm::BufferSizeType bytesNeeded = (numberOfBits + CHAR_BIT - 1) / CHAR_BIT; + const vtkm::BufferSizeType blocksNeeded = (bytesNeeded + BlockSize - 1) / BlockSize; + const vtkm::BufferSizeType numBytes = blocksNeeded * BlockSize; + + VTKM_LOG_F(vtkm::cont::LogLevel::MemCont, + "BitField Allocation: %llu bits, blocked up to %s bytes.", + static_cast(numberOfBits), + vtkm::cont::GetSizeString(static_cast(numBytes)).c_str()); + + this->Buffer.SetNumberOfBytes(numBytes, preserve, token); + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits = numberOfBits; +} + +void BitField::ReleaseResourcesExecution() +{ + this->Buffer.ReleaseDeviceResources(); +} + +void BitField::ReleaseResources() +{ + vtkm::cont::Token token; + this->Buffer.SetNumberOfBytes(0, vtkm::CopyFlag::Off, token); + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits = 0; +} + +void BitField::SyncControlArray() const +{ + vtkm::cont::Token token; + this->Buffer.ReadPointerHost(token); +} + +bool BitField::IsOnDevice(vtkm::cont::DeviceAdapterId device) const +{ + return this->Buffer.IsAllocatedOnDevice(device); +} + +vtkm::cont::DeviceAdapterId BitField::GetDeviceAdapterId() const +{ + DeviceCheckFunctor functor; + vtkm::ListForEach(functor, VTKM_DEFAULT_DEVICE_ADAPTER_LIST{}, this->Buffer); + return functor.FoundDevice; +} + +BitField::WritePortalType BitField::WritePortal() const +{ + vtkm::cont::Token token; + return WritePortalType(this->Buffer.WritePointerHost(token), + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits); +} + +BitField::ReadPortalType BitField::ReadPortal() const +{ + vtkm::cont::Token token; + return ReadPortalType(this->Buffer.ReadPointerHost(token), + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits); +} + +BitField::ReadPortalType BitField::PrepareForInput(vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const +{ + return ReadPortalType(this->Buffer.ReadPointerDevice(device, token), + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits); +} + +BitField::WritePortalType BitField::PrepareForOutput(vtkm::Id numBits, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const +{ + this->Allocate(numBits, vtkm::CopyFlag::Off, token); + return WritePortalType(this->Buffer.WritePointerDevice(device, token), + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits); +} + +BitField::WritePortalType BitField::PrepareForInPlace(vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const +{ + return WritePortalType(this->Buffer.WritePointerDevice(device, token), + detail::GetBitFieldMetaData(this->Buffer)->NumberOfBits); +} + +} +} // namespace vtkm::cont diff --git a/vtkm/cont/BitField.h b/vtkm/cont/BitField.h index 7a77b8950..1b9f2d2da 100644 --- a/vtkm/cont/BitField.h +++ b/vtkm/cont/BitField.h @@ -12,7 +12,7 @@ #define vtk_m_cont_BitField_h #include -#include +#include #include #include @@ -33,7 +33,18 @@ class BitField; namespace internal { + struct StorageTagBitField; + +struct VTKM_CONT_EXPORT BitFieldMetaData : vtkm::cont::internal::BufferMetaData +{ + vtkm::Id NumberOfBits = 0; + + VTKM_CONT ~BitFieldMetaData() override; + + VTKM_CONT std::unique_ptr DeepCopy() const override; +}; + } namespace detail @@ -88,6 +99,9 @@ struct BitCoordinate vtkm::Int32 BitOffset; // [0, bitsInWord) }; +VTKM_CONT_EXPORT vtkm::cont::internal::BitFieldMetaData* GetBitFieldMetaData( + const vtkm::cont::internal::Buffer& buffer); + /// Portal for performing bit or word operations on a BitField. /// /// This is the implementation used by BitPortal and BitPortalConst. @@ -131,18 +145,6 @@ public: protected: friend class vtkm::cont::BitField; - - /// Construct a BitPortal from an ArrayHandle with basic storage's portal. - template - VTKM_EXEC_CONT BitPortalBase(const PortalType& portal, vtkm::Id numberOfBits) - : Data{ portal.GetIteratorBegin() } - , NumberOfBits{ numberOfBits } - { - VTKM_STATIC_ASSERT_MSG(HasPointerAccess::value, - "Source portal must return a pointer from " - "GetIteratorBegin()."); - } - friend class vtkm::cont::internal::Storage; /// Construct a BitPortal from a raw array. @@ -513,13 +515,14 @@ struct IsValidWordTypeDeprecated } // end namespace detail -class BitField +class VTKM_CONT_EXPORT BitField { static constexpr vtkm::Id BlockSize = detail::BitFieldTraits::BlockSize; public: /// The type array handle used to store the bit data internally: - using ArrayHandleType = ArrayHandle; + using ArrayHandleType VTKM_DEPRECATED(1.6, "BitField now uses a Buffer to store data.") = + ArrayHandle; /// The BitPortal used in the control environment. using WritePortalType = detail::BitPortal; @@ -559,10 +562,7 @@ public: using IsValidWordTypeAtomicControl VTKM_DEPRECATED(1.6, "Use IsValidWordTypeAtomic instead.") = detail::BitFieldTraits::IsValidWordTypeAtomic; - VTKM_CONT BitField() - : Internals{ std::make_shared() } - { - } + VTKM_CONT BitField(); VTKM_CONT BitField(const BitField&) = default; VTKM_CONT BitField(BitField&&) noexcept = default; VTKM_CONT ~BitField() = default; @@ -570,22 +570,23 @@ public: VTKM_CONT BitField& operator=(BitField&&) noexcept = default; VTKM_CONT - bool operator==(const BitField& rhs) const { return this->Internals == rhs.Internals; } + bool operator==(const BitField& rhs) const { return this->Buffer == rhs.Buffer; } VTKM_CONT - bool operator!=(const BitField& rhs) const { return this->Internals != rhs.Internals; } + bool operator!=(const BitField& rhs) const { return this->Buffer != rhs.Buffer; } + + /// Return the internal `Buffer` used to store the `BitField`. + VTKM_CONT vtkm::cont::internal::Buffer GetBuffer() const { return this->Buffer; } /// Return the internal ArrayHandle used to store the BitField. - VTKM_CONT - ArrayHandleType& GetData() { return this->Internals->Data; } - - /// Return the internal ArrayHandle used to store the BitField. - VTKM_CONT - const ArrayHandleType& GetData() const { return this->Internals->Data; } + VTKM_CONT VTKM_DEPRECATED(1.6, "BitField now uses a Buffer to store data.") + ArrayHandle GetData() const + { + return vtkm::cont::ArrayHandle(&this->Buffer); + } /// Return the number of bits stored by this BitField. - VTKM_CONT - vtkm::Id GetNumberOfBits() const { return this->Internals->NumberOfBits; } + VTKM_CONT vtkm::Id GetNumberOfBits() const; /// Return the number of words (of @a WordType) stored in this bit fields. /// @@ -594,83 +595,69 @@ public: { VTKM_STATIC_ASSERT(IsValidWordType::value); static constexpr vtkm::Id WordBits = static_cast(sizeof(WordType) * CHAR_BIT); - return (this->Internals->NumberOfBits + WordBits - 1) / WordBits; + return (this->GetNumberOfBits() + WordBits - 1) / WordBits; } /// Allocate the requested number of bits. - VTKM_CONT - void Allocate(vtkm::Id numberOfBits) + VTKM_CONT void Allocate(vtkm::Id numberOfBits, + vtkm::CopyFlag preserve, + vtkm::cont::Token& token) const; + + /// Allocate the requested number of bits. + VTKM_CONT void Allocate(vtkm::Id numberOfBits, + vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const { - const vtkm::Id numWords = this->BitsToAllocatedStorageWords(numberOfBits); - - VTKM_LOG_F(vtkm::cont::LogLevel::MemCont, - "BitField Allocation: %llu bits, blocked up to %s.", - static_cast(numberOfBits), - vtkm::cont::GetSizeString( - static_cast(static_cast(numWords) * sizeof(WordTypeDefault))) - .c_str()); - - this->Internals->Data.Allocate(numWords); - this->Internals->NumberOfBits = numberOfBits; + vtkm::cont::Token token; + this->Allocate(numberOfBits, preserve, token); } /// Shrink the bit field to the requested number of bits. - VTKM_CONT - void Shrink(vtkm::Id numberOfBits) + VTKM_CONT VTKM_DEPRECATED(1.6, + "Use Allocate with preserve = On.") void Shrink(vtkm::Id numberOfBits) { - const vtkm::Id numWords = this->BitsToAllocatedStorageWords(numberOfBits); - this->Internals->Data.Shrink(numWords); - this->Internals->NumberOfBits = numberOfBits; + this->Allocate(numberOfBits, vtkm::CopyFlag::On); } /// Release all execution-side resources held by this BitField. - VTKM_CONT - void ReleaseResourcesExecution() { this->Internals->Data.ReleaseResourcesExecution(); } + VTKM_CONT void ReleaseResourcesExecution(); /// Release all resources held by this BitField and reset to empty. - VTKM_CONT - void ReleaseResources() - { - this->Internals->Data.ReleaseResources(); - this->Internals->NumberOfBits = 0; - } + VTKM_CONT void ReleaseResources(); /// Force the control array to sync with the last-used device. - VTKM_CONT - void SyncControlArray() const { this->Internals->Data.SyncControlArray(); } + VTKM_CONT void SyncControlArray() const; - /// The id of the device where the most up-to-date copy of the data is - /// currently resident. If the data is on the host, DeviceAdapterTagUndefined - /// is returned. - VTKM_CONT - DeviceAdapterId GetDeviceAdapterId() const { return this->Internals->Data.GetDeviceAdapterId(); } + /// Returns true if the `BitField`'s data is on the given device. If the data are on the given + /// device, then preparing for that device should not require any data movement. + /// + VTKM_CONT bool IsOnDevice(vtkm::cont::DeviceAdapterId device) const; + + /// Returns true if the `BitField`'s data is on the host. If the data are on the given + /// device, then calling `ReadPortal` or `WritePortal` should not require any data movement. + /// + VTKM_CONT bool IsOnHost() const + { + return this->IsOnDevice(vtkm::cont::DeviceAdapterTagUndefined{}); + } + + VTKM_CONT VTKM_DEPRECATED(1.6, "Data can be on multiple devices. Use IsOnDevice.") + vtkm::cont::DeviceAdapterId GetDeviceAdapterId() const; /// \brief Get a portal to the data that is usable from the control environment. /// /// As long as this portal is in scope, no one else will be able to read or write the BitField. - VTKM_CONT WritePortalType WritePortal() const - { - auto dataPortal = this->Internals->Data.WritePortal(); - return WritePortalType{ dataPortal, this->Internals->NumberOfBits }; - } + VTKM_CONT WritePortalType WritePortal() const; /// \brief Get a read-only portal to the data that is usable from the control environment. /// /// As long as this portal is in scope, no one else will be able to write in the BitField. - VTKM_CONT ReadPortalType ReadPortal() const - { - auto dataPortal = this->Internals->Data.ReadPortal(); - return ReadPortalType{ dataPortal, this->Internals->NumberOfBits }; - } + VTKM_CONT ReadPortalType ReadPortal() const; VTKM_CONT VTKM_DEPRECATED(1.6, "Use BitField::WritePortal() instead. " "Note that the returned portal will lock the array while it is in scope.") - detail::BitPortal GetPortalControl() - { - return detail::BitPortal{ this->Internals->Data.WritePortal(), this->Internals->NumberOfBits }; - } + detail::BitPortal GetPortalControl() { return this->WritePortal(); } /// Get a read-only portal to the data that is usable from the control /// environment. @@ -678,26 +665,15 @@ public: VTKM_DEPRECATED(1.6, "Use BitField::ReadPortal() instead. " "Note that the returned portal will lock the array while it is in scope.") - detail::BitPortalConst GetPortalConstControl() const - { - return detail::BitPortalConst{ this->Internals->Data.ReadPortal(), - this->Internals->NumberOfBits }; - } + detail::BitPortalConst GetPortalConstControl() const { return this->ReadPortal(); } /// Prepares this BitField to be used as an input to an operation in the /// execution environment. If necessary, copies data to the execution /// environment. Can throw an exception if this BitField does not yet contain /// any data. Returns a portal that can be used in code running in the /// execution environment. - template - VTKM_CONT typename ExecutionTypes::PortalConst PrepareForInput( - DeviceAdapterTag device, - vtkm::cont::Token& token) const - { - using PortalType = typename ExecutionTypes::PortalConst; - return PortalType{ this->Internals->Data.PrepareForInput(device, token), - this->Internals->NumberOfBits }; - } + VTKM_CONT ReadPortalType PrepareForInput(vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const; template VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForInput now requires a vtkm::cont::Token object.") @@ -714,24 +690,9 @@ public: /// that the array will be filled soon (i.e. before any other methods of this /// object are called). Returns a portal that can be used in code running in /// the execution environment. - template - VTKM_CONT typename ExecutionTypes::Portal - PrepareForOutput(vtkm::Id numBits, DeviceAdapterTag device, vtkm::cont::Token& token) const - { - using PortalType = typename ExecutionTypes::Portal; - const vtkm::Id numWords = this->BitsToAllocatedStorageWords(numBits); - - VTKM_LOG_F(vtkm::cont::LogLevel::MemExec, - "BitField Allocation: %llu bits, blocked up to %s.", - static_cast(numBits), - vtkm::cont::GetSizeString( - static_cast(static_cast(numWords) * sizeof(WordTypeDefault))) - .c_str()); - - auto portal = this->Internals->Data.PrepareForOutput(numWords, device, token); - this->Internals->NumberOfBits = numBits; - return PortalType{ portal, numBits }; - } + VTKM_CONT WritePortalType PrepareForOutput(vtkm::Id numBits, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const; template VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForOutput now requires a vtkm::cont::Token object.") @@ -747,15 +708,8 @@ public: /// the execution environment. Can throw an exception if this BitField does /// not yet contain any data. Returns a portal that can be used in code /// running in the execution environment. - template - VTKM_CONT typename ExecutionTypes::Portal PrepareForInPlace( - DeviceAdapterTag device, - vtkm::cont::Token& token) const - { - using PortalType = typename ExecutionTypes::Portal; - return PortalType{ this->Internals->Data.PrepareForInPlace(device, token), - this->Internals->NumberOfBits }; - } + VTKM_CONT WritePortalType PrepareForInPlace(vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) const; template VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForInPlace now requires a vtkm::cont::Token object.") @@ -767,27 +721,7 @@ public: } private: - /// Returns the number of words, padded out to respect BlockSize. - VTKM_CONT - static vtkm::Id BitsToAllocatedStorageWords(vtkm::Id numBits) - { - static constexpr vtkm::Id InternalWordSize = static_cast(sizeof(WordTypeDefault)); - - // Round up to BlockSize bytes: - const vtkm::Id bytesNeeded = (numBits + CHAR_BIT - 1) / CHAR_BIT; - const vtkm::Id blocksNeeded = (bytesNeeded + BlockSize - 1) / BlockSize; - const vtkm::Id numBytes = blocksNeeded * BlockSize; - const vtkm::Id numWords = numBytes / InternalWordSize; - return numWords; - } - - struct VTKM_ALWAYS_EXPORT InternalStruct - { - ArrayHandleType Data; - vtkm::Id NumberOfBits; - }; - - std::shared_ptr Internals; + mutable vtkm::cont::internal::Buffer Buffer; }; } } // end namespace vtkm::cont diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 5288799c9..20eaf866e 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -139,6 +139,7 @@ set(sources ArrayHandle.cxx ArrayHandleBasic.cxx ArrayHandleSOA.cxx + BitField.cxx ColorTablePresets.cxx DeviceAdapterTag.cxx EnvironmentTracker.cxx diff --git a/vtkm/cont/internal/Buffer.cxx b/vtkm/cont/internal/Buffer.cxx index 8d60b4dcc..4cd86ae85 100644 --- a/vtkm/cont/internal/Buffer.cxx +++ b/vtkm/cont/internal/Buffer.cxx @@ -545,7 +545,7 @@ Buffer::Buffer(const Buffer& src) } // Defined to prevent issues with CUDA -Buffer::Buffer(Buffer&& src) +Buffer::Buffer(Buffer&& src) noexcept : Internals(std::move(src.Internals)) { } @@ -561,7 +561,7 @@ Buffer& Buffer::operator=(const Buffer& src) } // Defined to prevent issues with CUDA -Buffer& Buffer::operator=(Buffer&& src) +Buffer& Buffer::operator=(Buffer&& src) noexcept { this->Internals = std::move(src.Internals); return *this; @@ -816,6 +816,15 @@ void Buffer::Reset(const vtkm::cont::internal::BufferInfo& bufferInfo) this->Internals->SetNumberOfBytes(lock, bufferInfo.GetSize()); } +void Buffer::ReleaseDeviceResources() const +{ + vtkm::cont::Token token; + + // Getting a write host buffer will invalidate any device arrays and preserve data + // on the host (copying if necessary). + this->WritePointerHost(token); +} + vtkm::cont::internal::BufferInfo Buffer::GetHostBufferInfo() const { LockType lock = this->Internals->GetLock(); diff --git a/vtkm/cont/internal/Buffer.h b/vtkm/cont/internal/Buffer.h index 6f6f5c07d..389340c8e 100644 --- a/vtkm/cont/internal/Buffer.h +++ b/vtkm/cont/internal/Buffer.h @@ -76,12 +76,12 @@ public: VTKM_CONT Buffer(); VTKM_CONT Buffer(const Buffer& src); - VTKM_CONT Buffer(Buffer&& src); + VTKM_CONT Buffer(Buffer&& src) noexcept; VTKM_CONT ~Buffer(); VTKM_CONT Buffer& operator=(const Buffer& src); - VTKM_CONT Buffer& operator=(Buffer&& src); + VTKM_CONT Buffer& operator=(Buffer&& src) noexcept; /// \brief Returns the number of bytes held by the buffer. /// @@ -222,6 +222,18 @@ public: /// VTKM_CONT void Reset(const vtkm::cont::internal::BufferInfo& buffer); + /// \brief Unallocates the buffer from all devices. + /// + /// This method preserves the data on the host even if the data must be transferred + /// there. + /// + /// Note that this method will not physically deallocate memory on a device that shares + /// a memory space with the host (since the data must be preserved on the host). This + /// is true even for memory spaces that page data between host and device. This method + /// will not attempt to unpage data from a device with shared memory. + /// + VTKM_CONT void ReleaseDeviceResources() const; + /// \brief Gets the `BufferInfo` object to the memory allocated on the host. /// VTKM_CONT vtkm::cont::internal::BufferInfo GetHostBufferInfo() const; diff --git a/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h b/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h index 2ecd77629..5f81d45af 100644 --- a/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h +++ b/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h @@ -321,7 +321,7 @@ public: if (numBits == 0) { - bits.Shrink(0); + bits.Allocate(0); return; } @@ -376,7 +376,7 @@ public: if (numBits == 0) { - bits.Shrink(0); + bits.Allocate(0); return; } diff --git a/vtkm/cont/testing/TestingBitField.h b/vtkm/cont/testing/TestingBitField.h index 28190ea03..15a255ff3 100644 --- a/vtkm/cont/testing/TestingBitField.h +++ b/vtkm/cont/testing/TestingBitField.h @@ -150,8 +150,7 @@ struct TestingBitField // NumBits should be rounded up to the nearest block of bytes, as defined in // the traits: - const vtkm::Id bytesInFieldData = - field.GetData().GetNumberOfValues() * static_cast(sizeof(vtkm::WordTypeDefault)); + const vtkm::Id bytesInFieldData = field.GetBuffer().GetNumberOfBytes(); const vtkm::Id blockSize = vtkm::cont::detail::BitFieldTraits::BlockSize; const vtkm::Id numBytes = (NUM_BITS + CHAR_BIT - 1) / CHAR_BIT;