1 //============================================================================ 2 // Copyright (c) Kitware, Inc. 3 // All rights reserved. 4 // See LICENSE.txt for details. 5 // This software is distributed WITHOUT ANY WARRANTY; without even 6 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 7 // PURPOSE. See the above copyright notice for more information. 8 // 9 // Copyright 2017 National Technology & Engineering Solutions of Sandia, LLC (NTESS). 10 // Copyright 2017 UT-Battelle, LLC. 11 // Copyright 2017 Los Alamos National Security. 12 // 13 // Under the terms of Contract DE-NA0003525 with NTESS, 14 // the U.S. Government retains certain rights in this software. 15 // 16 // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National 17 // Laboratory (LANL), the U.S. Government retains certain rights in 18 // this software. 19 //============================================================================ 20 #ifndef vtk_m_cont_VirtualObjectHandle_h 21 #define vtk_m_cont_VirtualObjectHandle_h 22 23 #include <vtkm/cont/DeviceAdapterListTag.h> 24 #include <vtkm/cont/ErrorBadType.h> 25 #include <vtkm/cont/ErrorBadValue.h> 26 #include <vtkm/cont/internal/DeviceAdapterListHelpers.h> 27 #include <vtkm/cont/internal/DeviceAdapterTag.h> 28 #include <vtkm/cont/internal/VirtualObjectTransfer.h> 29 30 #include <array> 31 #include <type_traits> 32 33 namespace vtkm 34 { 35 namespace cont 36 { 37 38 /// \brief Implements VTK-m's execution side <em> Virtual Methods </em> functionality. 39 /// 40 /// The template parameter \c VirtualBaseType is the base class that acts as the 41 /// interface. This base clase must inherit from \c vtkm::VirtualObjectBase. See the 42 /// documentation of that class to see the other requirements. 43 /// 44 /// A derived object can be bound to the handle either during construction or using the \c Reset 45 /// function on previously constructed handles. These function accept a control side pointer to 46 /// the derived class object, a boolean flag stating if the handle should acquire ownership of 47 /// the object (i.e. manage the lifetime), and a type-list of device adapter tags where the object 48 /// is expected to be used. 49 /// 50 /// To get an execution side pointer call the \c PrepareForExecution function. The device adapter 51 /// passed to this function should be one of the device in the list passed during the set up of 52 /// the handle. 53 /// 54 /// 55 /// \sa vtkm::VirtualObjectBase 56 /// 57 template <typename VirtualBaseType> 58 class VTKM_ALWAYS_EXPORT VirtualObjectHandle 59 { 60 VTKM_STATIC_ASSERT_MSG((std::is_base_of<vtkm::VirtualObjectBase, VirtualBaseType>::value), 61 "All virtual objects must be subclass of vtkm::VirtualObjectBase."); 62 63 public: VirtualObjectHandle()64 VTKM_CONT VirtualObjectHandle() 65 : Internals(new InternalStruct) 66 { 67 } 68 69 template <typename VirtualDerivedType, 70 typename DeviceAdapterList = VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG> 71 VTKM_CONT explicit VirtualObjectHandle(VirtualDerivedType* derived, 72 bool acquireOwnership = true, 73 DeviceAdapterList devices = DeviceAdapterList()) Internals(new InternalStruct)74 : Internals(new InternalStruct) 75 { 76 this->Reset(derived, acquireOwnership, devices); 77 } 78 79 /// Get if in a valid state (a target is bound) GetValid()80 VTKM_CONT bool GetValid() const { return this->Internals->VirtualObject != nullptr; } 81 82 /// Get if this handle owns the control side target object OwnsObject()83 VTKM_CONT bool OwnsObject() const { return this->Internals->Owner; } 84 85 /// Get the control side pointer to the virtual object Get()86 VTKM_CONT VirtualBaseType* Get() const { return this->Internals->VirtualObject; } 87 88 /// Reset the underlying derived type object 89 template <typename VirtualDerivedType, 90 typename DeviceAdapterList = VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG> 91 VTKM_CONT void Reset(VirtualDerivedType* derived, 92 bool acquireOwnership = true, 93 DeviceAdapterList devices = DeviceAdapterList()) 94 { 95 this->Reset(); 96 if (derived) 97 { 98 VTKM_STATIC_ASSERT_MSG((std::is_base_of<VirtualBaseType, VirtualDerivedType>::value), 99 "Tried to bind a type that is not a subclass of the base class."); 100 101 this->Internals->VirtualObject = derived; 102 this->Internals->Owner = acquireOwnership; 103 vtkm::cont::internal::ForEachValidDevice(devices, 104 CreateTransferInterface<VirtualDerivedType>(), 105 this->Internals->Transfers.data(), 106 derived); 107 } 108 } 109 Reset()110 void Reset() { this->Internals->Reset(); } 111 112 /// Release all the execution side resources ReleaseExecutionResources()113 VTKM_CONT void ReleaseExecutionResources() { this->Internals->ReleaseExecutionResources(); } 114 115 /// Get a valid \c VirtualBaseType* with the current control side state for \c deviceId. 116 /// VirtualObjectHandle and the returned pointer are analogous to ArrayHandle and Portal 117 /// The returned pointer will be invalidated if: 118 /// 1. A new pointer is requested for a different deviceId 119 /// 2. VirtualObjectHandle is destroyed 120 /// 3. Reset or ReleaseResources is called 121 /// PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId)122 VTKM_CONT const VirtualBaseType* PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId) const 123 { 124 if (!this->GetValid()) 125 { 126 throw vtkm::cont::ErrorBadValue("No target object bound"); 127 } 128 129 if (!this->Internals->Current || this->Internals->Current->GetDeviceId() != deviceId) 130 { 131 if (!this->Internals->Transfers[static_cast<std::size_t>(deviceId.GetValue())]) 132 { 133 std::string msg = 134 "VTK-m was asked to transfer a VirtualObjectHandle for execution on DeviceAdapter '" + 135 deviceId.GetName() + "' (" + std::to_string(deviceId.GetValue()) + 136 "). It can't as this VirtualObjectHandle was not constructed/bound with this " 137 "DeviceAdapter in the list of valid DeviceAdapters."; 138 throw vtkm::cont::ErrorBadDevice(msg); 139 } 140 141 if (this->Internals->Current) 142 { 143 this->Internals->Current->ReleaseResources(); 144 } 145 this->Internals->Current = 146 this->Internals->Transfers[static_cast<std::size_t>(deviceId.GetValue())].get(); 147 } 148 149 return this->Internals->Current->PrepareForExecution(); 150 } 151 152 153 private: 154 class TransferInterface 155 { 156 public: 157 VTKM_CONT virtual ~TransferInterface() = default; 158 159 VTKM_CONT virtual vtkm::cont::DeviceAdapterId GetDeviceId() const = 0; 160 VTKM_CONT virtual const VirtualBaseType* PrepareForExecution() = 0; 161 VTKM_CONT virtual void ReleaseResources() = 0; 162 }; 163 164 template <typename VirtualDerivedType, typename DeviceAdapter> 165 class TransferInterfaceImpl : public TransferInterface 166 { 167 public: TransferInterfaceImpl(const VirtualDerivedType * virtualObject)168 VTKM_CONT TransferInterfaceImpl(const VirtualDerivedType* virtualObject) 169 : LastModifiedCount(-1) 170 , VirtualObject(virtualObject) 171 , Transfer(virtualObject) 172 { 173 } 174 GetDeviceId()175 VTKM_CONT vtkm::cont::DeviceAdapterId GetDeviceId() const override { return DeviceAdapter(); } 176 PrepareForExecution()177 VTKM_CONT const VirtualBaseType* PrepareForExecution() override 178 { 179 vtkm::Id modifiedCount = this->VirtualObject->GetModifiedCount(); 180 bool updateData = (this->LastModifiedCount != modifiedCount); 181 const VirtualBaseType* executionObject = this->Transfer.PrepareForExecution(updateData); 182 this->LastModifiedCount = modifiedCount; 183 return executionObject; 184 } 185 ReleaseResources()186 VTKM_CONT void ReleaseResources() override { this->Transfer.ReleaseResources(); } 187 188 private: 189 vtkm::Id LastModifiedCount; 190 const VirtualDerivedType* VirtualObject; 191 vtkm::cont::internal::VirtualObjectTransfer<VirtualDerivedType, DeviceAdapter> Transfer; 192 }; 193 194 template <typename VirtualDerivedType> 195 struct CreateTransferInterface 196 { 197 template <typename DeviceAdapter> operatorCreateTransferInterface198 VTKM_CONT void operator()(DeviceAdapter device, 199 std::unique_ptr<TransferInterface>* transfers, 200 const VirtualDerivedType* virtualObject) const 201 { 202 if (!device.IsValueValid()) 203 { 204 throwFailedRuntimeDeviceTransfer("VirtualObjectHandle", device); 205 } 206 using TransferImpl = TransferInterfaceImpl<VirtualDerivedType, DeviceAdapter>; 207 transfers[device.GetValue()].reset(new TransferImpl(virtualObject)); 208 } 209 }; 210 211 struct InternalStruct 212 { 213 VirtualBaseType* VirtualObject = nullptr; 214 bool Owner = false; 215 std::array<std::unique_ptr<TransferInterface>, VTKM_MAX_DEVICE_ADAPTER_ID> Transfers; 216 TransferInterface* Current = nullptr; 217 ReleaseExecutionResourcesInternalStruct218 VTKM_CONT void ReleaseExecutionResources() 219 { 220 if (this->Current) 221 { 222 this->Current->ReleaseResources(); 223 this->Current = nullptr; 224 } 225 } 226 ResetInternalStruct227 VTKM_CONT void Reset() 228 { 229 this->ReleaseExecutionResources(); 230 for (auto& transfer : this->Transfers) 231 { 232 transfer.reset(nullptr); 233 } 234 if (this->Owner) 235 { 236 delete this->VirtualObject; 237 } 238 this->VirtualObject = nullptr; 239 this->Owner = false; 240 } 241 ~InternalStructInternalStruct242 VTKM_CONT ~InternalStruct() { this->Reset(); } 243 }; 244 245 std::shared_ptr<InternalStruct> Internals; 246 }; 247 } 248 } // vtkm::cont 249 250 #endif // vtk_m_cont_VirtualObjectHandle_h 251