Advertisement
Guest User

Untitled

a guest
Apr 25th, 2017
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.18 KB | None | 0 0
  1. # Execution side virtual methods in VTK-m
  2.  
  3. The following is a description of execution side virtual methods as currently
  4. implemented in VTK-m.
  5.  
  6.  
  7. The majority of this implementation resides in the following class:
  8. ```c++
  9. template<typename VirtualObject>
  10. class VirtualObjectCache;
  11. ```
  12.  
  13. The template parameter `VirtualObject` is the class that acts as the interface.
  14. Since the interface is application dependent, it has to be implemented on a
  15. case-by-case basis. Following is a set of guide-lines for implementing virtual
  16. object classes:
  17. 1. Create a `const void*` member variable that will hold a reference to a
  18. concrete class object.
  19. 2. For each virtual-like method:
  20. a. Create a typedef for a function with the same signature, except for an
  21. extra `const void*` argument.
  22. b. Create a function pointer member variable with the type of the associated
  23. typedef from a.
  24. c. Create an implementation of the method, that calls the associated
  25. function pointer with the `void*` to the target class as one of the
  26. arguments.
  27. 3. Create the following template function:
  28. ```c++
  29. template<typename TargetClass>
  30. VTKM_EXEC void Bind(const TargetClass *deviceTarget);
  31. ```
  32. This function should copy `deviceTarget` to the `const void*` member from 1,
  33. and, set the function pointers from 2b to functions that cast their
  34. `const void*` argument to `const TargetClass*` and call the corresponding
  35. member function on it.
  36.  
  37. Both `VirtualObject` and `TargetClass` objects should be bitwise copyable, as
  38. that is how they are transferred between the host and the device.
  39.  
  40. Following is an example `VirtualObject` from the implementation of implicit
  41. functions in VTK-m:
  42. ```c++
  43.  
  44. class VTKM_ALWAYS_EXPORT ImplicitFunction
  45. {
  46. public:
  47. ImplicitFunction()
  48. : Function(nullptr), ValueCaller(nullptr), GradientCaller(nullptr)
  49. { }
  50.  
  51. // Step 2c. methods that call the function pointers
  52. VTKM_EXEC
  53. FloatDefault Value(FloatDefault x, FloatDefault y, FloatDefault z) const
  54. {
  55. return this->ValueCaller(this->Function, x, y, z);
  56. }
  57.  
  58. VTKM_EXEC
  59. vtkm::Vec<FloatDefault, 3> Gradient(FloatDefault x, FloatDefault y,
  60. FloatDefault z) const
  61. {
  62. return this->GradientCaller(this->Function, x, y, z);
  63. }
  64.  
  65. // Step 3. The bind function. Note that assigning labda functions without
  66. // captures is a consice way to assign the function pointers.
  67. template<typename T>
  68. VTKM_EXEC
  69. void Bind(const T *function)
  70. {
  71. this->Function = function;
  72. this->ValueCaller =
  73. [](const void *t, FloatDefault x, FloatDefault y, FloatDefault z) {
  74. return static_cast<const T*>(t)->Value(x, y, z);
  75. };
  76. this->GradientCaller =
  77. [](const void *t, FloatDefault x, FloatDefault y, FloatDefault z) {
  78. return static_cast<const T*>(t)->Gradient(x, y, z);
  79. };
  80. }
  81.  
  82. private:
  83. // Step 2a. Function Signatures
  84. using ValueCallerSig =
  85. FloatDefault(const void*, FloatDefault, FloatDefault, FloatDefault);
  86. using GradientCallerSig =
  87. vtkm::Vec<FloatDefault, 3>(const void*, FloatDefault, FloatDefault, FloatDefault);
  88.  
  89. // Step 1. Concrete class reference
  90. const void *Function;
  91.  
  92. // Step 2b. Function pointers
  93. ValueCallerSig *ValueCaller;
  94. GradientCallerSig *GradientCaller;
  95. };
  96. ```
  97.  
  98. The `VirtualObjectCache` class provides the following methods:
  99.  
  100. `bool GetValid() const`
  101. Returns true if the object is in a valid state, i.e. it has been bound to a
  102. concrete object.
  103.  
  104. ```c++
  105. template<typename TargetClass, typename DeviceAdapterList>
  106. void Bind(const TargetClass *target, DeviceAdapterList devices)
  107. ```
  108. Binds to the concrete object `target`. Note that the lifetime of `target`
  109. is not managed by this class. It is the responsibility of the caller to ensure
  110. that `target` is valid for as long as it is bound.
  111. This function also takes an optional type-list of device adapters
  112. where the virtual object may potentially be used from. By default
  113. it is set to `VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG`.
  114. The device-list is required because in typical use cases, the device a virtual
  115. object will be used from is not known at the point of binding.
  116. For each device in the list this function stores references to functions that
  117. implement transferring and maintaining of the concrete object's state on the
  118. device. These functions are provided by the template class:
  119. ```c++
  120. template <typename VirtualObject, typename TargetClass, typename DeviceAdapter>
  121. struct VirtualObjectTransfer
  122. ```
  123. This class will be described later.
  124.  
  125. ```c++
  126. template<typename DeviceAdapter>
  127. VirtualObject GetVirtualObject(DeviceAdapter, bool updateCache)
  128. ```
  129. Returns an instance of `VirtualObject`, bound to the `target` specified in a
  130. previous call to `Bind`, and that can be used on the `DeviceAdapter` device.
  131. Since creating a `VirtualObject` on the device can be an expensive operation,
  132. the `VirtualObject` is internally cached. Any subsequent calls to this function
  133. for the same `DeviceAdapter` will return the cached value. Set `updateCache` to
  134. true if the cache should be updated to the latest state. Updating the state is
  135. still faster than creating a new state.
  136.  
  137. `void Reset()`:
  138. Resets the object to its default constructed state.
  139.  
  140.  
  141. As mentioned above the logic of transferring the concrete object's state to the
  142. device and creating the device specific `VirtualObject` objects is implemented
  143. in the `VirtualObjectTransfer` class.
  144. ```c++
  145. template <typename VirtualObject, typename TargetClass, typename DeviceAdapter>
  146. struct VirtualObjectTransfer
  147. ```
  148. This template class is specialized for each of the device adapters. It provides
  149. the following static member functions:
  150.  
  151. `static void* Create(VirtualObject &object, const void *hostTarget)`: Takes a
  152. `const void*` to the host target object, transfers it to the device, binds it
  153. to a `VirtualObject` on the device by calling its `Bind` function, assigns
  154. it to `object`, and returns a `void*` to an internal structure that maintains
  155. this state on the device.
  156.  
  157. `static void Update(void *deviceState, const void *target)`: Update the device
  158. state with the current state of the concrete object, `target`, on the host.
  159.  
  160. `static void Cleanup(void *deviceState)`: Perform cleanup of the device state
  161. by releasing all held resources on the device.
  162.  
  163. When the `VirtualObjectCache::Bind` method is called, it instantiates
  164. `VirtualObjectTransfer` with the associated types, and for each of the devices
  165. stores pointers to the above functions in a table indexed by the
  166. `DeviceAdapterId`. When `VirtualObjectCache::GetVirtualObject` is called,
  167. the `DeviceAdapterId` is used to lookup the associated `Create` in this table.
  168.  
  169.  
  170. Bellow are some guidelines to implement your own Polymorphic classes using
  171. `VirtualObjectCache`.
  172.  
  173. The first step is to implement the `VirtualObject` class that will act as
  174. the interface. Detailed guideline for creating this class is given above.
  175. For example, implicit functions use the `vtkm::exec::ImplicitFunction` class
  176. shown above.
  177.  
  178. Instantiate the `VirtualObjectCache` template with the `VirtualObject` type.
  179. Typically another class is created that contains a member object of this type.
  180. One object of this class is required per concrete object to be bound.
  181.  
  182. Whenever a concrete object is to be bound, call the `Bind` function, with a
  183. list of possible device adapters. When the device where the virtual object will
  184. be used is know, call the `GetVirtualObject` and pass the returned object to
  185. the execution environment.
  186.  
  187.  
  188. ## VTK-m implicit functions example
  189.  
  190. Each concrete implicit function such as `Plane` and `Sphere`, internally
  191. maintains a `VirtualObjectCache` bound to itself. They provide the following
  192. functions related to virtual methods:
  193.  
  194. ```c++
  195. template<typename DeviceAdapter>
  196. vtkm::exec::ImplicitFunction PrepareForExecution(DeviceAdapter device) const
  197. ```
  198. Returns an instance of `vtkm::exec::ImplicitFunction` that can be used
  199. on the execution side.
  200.  
  201. ```c++
  202. template<typename DeviceAdapterList>
  203. void ResetDevices(DeviceAdapterList devices);
  204. ```
  205. By default, `PrepareForExecution` can only work for the default devices. The
  206. device adapters list used during binding can be updated using this function.
  207.  
  208. To enable filters and other code to store any of the concrete implicit
  209. functions, all of the implicit functions inherit an abstract base class
  210. `vtkm::cont::ImplicitFunction`.
  211. This class has two member variables - `bool Modified` which keeps track of the
  212. modified status of the implicit functions so that the cache can be updated when
  213. required, and a reference to the `VirtualObjectCache` as
  214. `std::unique_ptr<vtkm::cont::VirtualObjectCache<vtkm::exec::ImplicitFunction>> Cache`
  215. This cache is stored as a reference instead of a member object because the
  216. state of this class is part of the state of an implicit function and using a
  217. reference reduces the amount of memory that needs to be copied to the device.
  218. (Though the pointer value is copied to the device it is never accessed on the
  219. device).
  220.  
  221. To prevent code duplication while implementing different implicit functions
  222. a helper class is provided that encapsulates the code dealing with the
  223. `VirtualObjectCache`.
  224. ```c++
  225. template<typename Derived>
  226. class VTKM_ALWAYS_EXPORT ImplicitFunctionImpl : public ImplicitFunction
  227.  
  228. ```
  229. This class derives from `vtkm::cont::ImplicitFunction` and is implemented
  230. using the CRTP pattern. All the implicit functions inherit from this class,
  231. specialized by the implicit function type.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement