Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Execution side virtual methods in VTK-m
- The following is a description of execution side virtual methods as currently
- implemented in VTK-m.
- The majority of this implementation resides in the following class:
- ```c++
- template<typename VirtualObject>
- class VirtualObjectCache;
- ```
- The template parameter `VirtualObject` is the class that acts as the interface.
- Since the interface is application dependent, it has to be implemented on a
- case-by-case basis. Following is a set of guide-lines for implementing virtual
- object classes:
- 1. Create a `const void*` member variable that will hold a reference to a
- concrete class object.
- 2. For each virtual-like method:
- a. Create a typedef for a function with the same signature, except for an
- extra `const void*` argument.
- b. Create a function pointer member variable with the type of the associated
- typedef from a.
- c. Create an implementation of the method, that calls the associated
- function pointer with the `void*` to the target class as one of the
- arguments.
- 3. Create the following template function:
- ```c++
- template<typename TargetClass>
- VTKM_EXEC void Bind(const TargetClass *deviceTarget);
- ```
- This function should copy `deviceTarget` to the `const void*` member from 1,
- and, set the function pointers from 2b to functions that cast their
- `const void*` argument to `const TargetClass*` and call the corresponding
- member function on it.
- Both `VirtualObject` and `TargetClass` objects should be bitwise copyable, as
- that is how they are transferred between the host and the device.
- Following is an example `VirtualObject` from the implementation of implicit
- functions in VTK-m:
- ```c++
- class VTKM_ALWAYS_EXPORT ImplicitFunction
- {
- public:
- ImplicitFunction()
- : Function(nullptr), ValueCaller(nullptr), GradientCaller(nullptr)
- { }
- // Step 2c. methods that call the function pointers
- VTKM_EXEC
- FloatDefault Value(FloatDefault x, FloatDefault y, FloatDefault z) const
- {
- return this->ValueCaller(this->Function, x, y, z);
- }
- VTKM_EXEC
- vtkm::Vec<FloatDefault, 3> Gradient(FloatDefault x, FloatDefault y,
- FloatDefault z) const
- {
- return this->GradientCaller(this->Function, x, y, z);
- }
- // Step 3. The bind function. Note that assigning labda functions without
- // captures is a consice way to assign the function pointers.
- template<typename T>
- VTKM_EXEC
- void Bind(const T *function)
- {
- this->Function = function;
- this->ValueCaller =
- [](const void *t, FloatDefault x, FloatDefault y, FloatDefault z) {
- return static_cast<const T*>(t)->Value(x, y, z);
- };
- this->GradientCaller =
- [](const void *t, FloatDefault x, FloatDefault y, FloatDefault z) {
- return static_cast<const T*>(t)->Gradient(x, y, z);
- };
- }
- private:
- // Step 2a. Function Signatures
- using ValueCallerSig =
- FloatDefault(const void*, FloatDefault, FloatDefault, FloatDefault);
- using GradientCallerSig =
- vtkm::Vec<FloatDefault, 3>(const void*, FloatDefault, FloatDefault, FloatDefault);
- // Step 1. Concrete class reference
- const void *Function;
- // Step 2b. Function pointers
- ValueCallerSig *ValueCaller;
- GradientCallerSig *GradientCaller;
- };
- ```
- The `VirtualObjectCache` class provides the following methods:
- `bool GetValid() const`
- Returns true if the object is in a valid state, i.e. it has been bound to a
- concrete object.
- ```c++
- template<typename TargetClass, typename DeviceAdapterList>
- void Bind(const TargetClass *target, DeviceAdapterList devices)
- ```
- Binds to the concrete object `target`. Note that the lifetime of `target`
- is not managed by this class. It is the responsibility of the caller to ensure
- that `target` is valid for as long as it is bound.
- This function also takes an optional type-list of device adapters
- where the virtual object may potentially be used from. By default
- it is set to `VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG`.
- The device-list is required because in typical use cases, the device a virtual
- object will be used from is not known at the point of binding.
- For each device in the list this function stores references to functions that
- implement transferring and maintaining of the concrete object's state on the
- device. These functions are provided by the template class:
- ```c++
- template <typename VirtualObject, typename TargetClass, typename DeviceAdapter>
- struct VirtualObjectTransfer
- ```
- This class will be described later.
- ```c++
- template<typename DeviceAdapter>
- VirtualObject GetVirtualObject(DeviceAdapter, bool updateCache)
- ```
- Returns an instance of `VirtualObject`, bound to the `target` specified in a
- previous call to `Bind`, and that can be used on the `DeviceAdapter` device.
- Since creating a `VirtualObject` on the device can be an expensive operation,
- the `VirtualObject` is internally cached. Any subsequent calls to this function
- for the same `DeviceAdapter` will return the cached value. Set `updateCache` to
- true if the cache should be updated to the latest state. Updating the state is
- still faster than creating a new state.
- `void Reset()`:
- Resets the object to its default constructed state.
- As mentioned above the logic of transferring the concrete object's state to the
- device and creating the device specific `VirtualObject` objects is implemented
- in the `VirtualObjectTransfer` class.
- ```c++
- template <typename VirtualObject, typename TargetClass, typename DeviceAdapter>
- struct VirtualObjectTransfer
- ```
- This template class is specialized for each of the device adapters. It provides
- the following static member functions:
- `static void* Create(VirtualObject &object, const void *hostTarget)`: Takes a
- `const void*` to the host target object, transfers it to the device, binds it
- to a `VirtualObject` on the device by calling its `Bind` function, assigns
- it to `object`, and returns a `void*` to an internal structure that maintains
- this state on the device.
- `static void Update(void *deviceState, const void *target)`: Update the device
- state with the current state of the concrete object, `target`, on the host.
- `static void Cleanup(void *deviceState)`: Perform cleanup of the device state
- by releasing all held resources on the device.
- When the `VirtualObjectCache::Bind` method is called, it instantiates
- `VirtualObjectTransfer` with the associated types, and for each of the devices
- stores pointers to the above functions in a table indexed by the
- `DeviceAdapterId`. When `VirtualObjectCache::GetVirtualObject` is called,
- the `DeviceAdapterId` is used to lookup the associated `Create` in this table.
- Bellow are some guidelines to implement your own Polymorphic classes using
- `VirtualObjectCache`.
- The first step is to implement the `VirtualObject` class that will act as
- the interface. Detailed guideline for creating this class is given above.
- For example, implicit functions use the `vtkm::exec::ImplicitFunction` class
- shown above.
- Instantiate the `VirtualObjectCache` template with the `VirtualObject` type.
- Typically another class is created that contains a member object of this type.
- One object of this class is required per concrete object to be bound.
- Whenever a concrete object is to be bound, call the `Bind` function, with a
- list of possible device adapters. When the device where the virtual object will
- be used is know, call the `GetVirtualObject` and pass the returned object to
- the execution environment.
- ## VTK-m implicit functions example
- Each concrete implicit function such as `Plane` and `Sphere`, internally
- maintains a `VirtualObjectCache` bound to itself. They provide the following
- functions related to virtual methods:
- ```c++
- template<typename DeviceAdapter>
- vtkm::exec::ImplicitFunction PrepareForExecution(DeviceAdapter device) const
- ```
- Returns an instance of `vtkm::exec::ImplicitFunction` that can be used
- on the execution side.
- ```c++
- template<typename DeviceAdapterList>
- void ResetDevices(DeviceAdapterList devices);
- ```
- By default, `PrepareForExecution` can only work for the default devices. The
- device adapters list used during binding can be updated using this function.
- To enable filters and other code to store any of the concrete implicit
- functions, all of the implicit functions inherit an abstract base class
- `vtkm::cont::ImplicitFunction`.
- This class has two member variables - `bool Modified` which keeps track of the
- modified status of the implicit functions so that the cache can be updated when
- required, and a reference to the `VirtualObjectCache` as
- `std::unique_ptr<vtkm::cont::VirtualObjectCache<vtkm::exec::ImplicitFunction>> Cache`
- This cache is stored as a reference instead of a member object because the
- state of this class is part of the state of an implicit function and using a
- reference reduces the amount of memory that needs to be copied to the device.
- (Though the pointer value is copied to the device it is never accessed on the
- device).
- To prevent code duplication while implementing different implicit functions
- a helper class is provided that encapsulates the code dealing with the
- `VirtualObjectCache`.
- ```c++
- template<typename Derived>
- class VTKM_ALWAYS_EXPORT ImplicitFunctionImpl : public ImplicitFunction
- ```
- This class derives from `vtkm::cont::ImplicitFunction` and is implemented
- using the CRTP pattern. All the implicit functions inherit from this class,
- specialized by the implicit function type.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement