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