1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //
6 //  This software is distributed WITHOUT ANY WARRANTY; without even
7 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 //  PURPOSE.  See the above copyright notice for more information.
9 //============================================================================
10 #ifndef vtk_m_cont_VirtualObjectHandle_h
11 #define vtk_m_cont_VirtualObjectHandle_h
12 
13 #include <vtkm/cont/DeviceAdapterList.h>
14 #include <vtkm/cont/ExecutionAndControlObjectBase.h>
15 #include <vtkm/cont/internal/DeviceAdapterListHelpers.h>
16 #include <vtkm/cont/internal/VirtualObjectTransfer.h>
17 
18 #include <array>
19 #include <type_traits>
20 
21 namespace vtkm
22 {
23 namespace cont
24 {
25 
26 namespace internal
27 {
28 struct CreateTransferInterface
29 {
30   template <typename VirtualDerivedType, typename DeviceAdapter>
operatorCreateTransferInterface31   VTKM_CONT inline void operator()(DeviceAdapter device,
32                                    internal::TransferState* transfers,
33                                    const VirtualDerivedType* virtualObject) const
34   {
35     using TransferImpl = TransferInterfaceImpl<VirtualDerivedType, DeviceAdapter>;
36     auto i = static_cast<std::size_t>(device.GetValue());
37     transfers->DeviceTransferState[i].reset(new TransferImpl(virtualObject));
38   }
39 };
40 }
41 
42 /// \brief Implements VTK-m's execution side <em> Virtual Methods </em> functionality.
43 ///
44 /// The template parameter \c VirtualBaseType is the base class that acts as the
45 /// interface. This base clase must inherit from \c vtkm::VirtualObjectBase. See the
46 /// documentation of that class to see the other requirements.
47 ///
48 /// A derived object can be bound to the handle either during construction or using the \c Reset
49 /// function on previously constructed handles. These function accept a control side pointer to
50 /// the derived class object, a boolean flag stating if the handle should acquire ownership of
51 /// the object (i.e. manage the lifetime), and a type-list of device adapter tags where the object
52 /// is expected to be used.
53 ///
54 /// To get an execution side pointer call the \c PrepareForExecution function. The device adapter
55 /// passed to this function should be one of the device in the list passed during the set up of
56 /// the handle.
57 ///
58 ///
59 /// \sa vtkm::VirtualObjectBase
60 ///
61 template <typename VirtualBaseType>
62 class VTKM_ALWAYS_EXPORT VirtualObjectHandle : public vtkm::cont::ExecutionAndControlObjectBase
63 {
64   VTKM_STATIC_ASSERT_MSG((std::is_base_of<vtkm::VirtualObjectBase, VirtualBaseType>::value),
65                          "All virtual objects must be subclass of vtkm::VirtualObjectBase.");
66 
67 public:
VirtualObjectHandle()68   VTKM_CONT VirtualObjectHandle()
69     : Internals(std::make_shared<internal::TransferState>())
70   {
71   }
72 
73   template <typename VirtualDerivedType,
74             typename DeviceAdapterList = VTKM_DEFAULT_DEVICE_ADAPTER_LIST>
75   VTKM_CONT explicit VirtualObjectHandle(VirtualDerivedType* derived,
76                                          bool acquireOwnership = true,
77                                          DeviceAdapterList devices = DeviceAdapterList())
Internals(std::make_shared<internal::TransferState> ())78     : Internals(std::make_shared<internal::TransferState>())
79   {
80     this->Reset(derived, acquireOwnership, devices);
81   }
82 
83   /// Get if in a valid state (a target is bound)
GetValid()84   VTKM_CONT bool GetValid() const { return this->Internals->HostPtr() != nullptr; }
85 
86 
87   /// Get if this handle owns the control side target object
OwnsObject()88   VTKM_CONT bool OwnsObject() const { return this->Internals->WillReleaseHostPointer(); }
89 
90   /// Get the control side pointer to the virtual object
Get()91   VTKM_CONT VirtualBaseType* Get() const
92   {
93     return static_cast<VirtualBaseType*>(this->Internals->HostPtr());
94   }
95 
96   /// Reset the underlying derived type object
97   template <typename VirtualDerivedType,
98             typename DeviceAdapterList = VTKM_DEFAULT_DEVICE_ADAPTER_LIST>
99   VTKM_CONT void Reset(VirtualDerivedType* derived,
100                        bool acquireOwnership = true,
101                        DeviceAdapterList devices = DeviceAdapterList())
102   {
103     VTKM_DEPRECATED_SUPPRESS_BEGIN
104     VTKM_STATIC_ASSERT_MSG((std::is_base_of<VirtualBaseType, VirtualDerivedType>::value),
105                            "Tried to bind a type that is not a subclass of the base class.");
106 
107     if (acquireOwnership)
108     {
109       auto deleter = [](void* p) { delete static_cast<VirtualBaseType*>(p); };
110       this->Internals->UpdateHost(derived, deleter);
111     }
112     else
113     {
114       this->Internals->UpdateHost(derived, nullptr);
115     }
116 
117     if (derived)
118     {
119       vtkm::cont::internal::ForEachValidDevice(
120         devices, internal::CreateTransferInterface(), this->Internals.get(), derived);
121     }
122     VTKM_DEPRECATED_SUPPRESS_END
123   }
124 
125   /// Release all host and execution side resources
ReleaseResources()126   VTKM_CONT void ReleaseResources() { this->Internals->ReleaseResources(); }
127 
128   /// Release all the execution side resources
ReleaseExecutionResources()129   VTKM_CONT void ReleaseExecutionResources() { this->Internals->ReleaseExecutionResources(); }
130 
131   /// Get a valid \c VirtualBaseType* with the current control side state for \c deviceId.
132   /// VirtualObjectHandle and the returned pointer are analogous to ArrayHandle and Portal
133   /// The returned pointer will be invalidated if:
134   /// 1. A new pointer is requested for a different deviceId
135   /// 2. VirtualObjectHandle is destroyed
136   /// 3. Reset or ReleaseResources is called
137   ///
PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId,vtkm::cont::Token &)138   VTKM_CONT const VirtualBaseType* PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId,
139                                                        vtkm::cont::Token&) const
140   {
141     const bool validId = this->Internals->DeviceIdIsValid(deviceId);
142     if (!validId)
143     { //can't be reached since DeviceIdIsValid will through an exception
144       //if deviceId is not valid
145       return nullptr;
146     }
147 
148     return static_cast<const VirtualBaseType*>(this->Internals->PrepareForExecution(deviceId));
149   }
150 
151   VTKM_CONT
152   VTKM_DEPRECATED(1.6, "PrepareForExecution now requires a vtkm::cont::Token object.")
PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId)153   const VirtualBaseType* PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId) const
154   {
155     vtkm::cont::Token token;
156     return this->PrepareForExecution(deviceId, token);
157   }
158 
159   /// Used as part of the \c ExecutionAndControlObjectBase interface. Returns the same pointer
160   /// as \c Get.
PrepareForControl()161   VTKM_CONT const VirtualBaseType* PrepareForControl() const { return this->Get(); }
162 
163 private:
164   std::shared_ptr<internal::TransferState> Internals;
165 };
166 }
167 } // vtkm::cont
168 
169 #endif // vtk_m_cont_VirtualObjectHandle_h
170