Hiding VTK-m filters in ParaView

Hi there!

As a part of a long term task to further integrate VTK-m into ParaView, we are trying to expose VTK-m filters in the ParaView UI not as independent filters (such as vtkmContour) but instead under the same name as its corresponding PV filter (such as Contour).

So far we have came up with the following strategy:

In VTK, we override VTK filters that has its corresponding in VTK-m using the vtkObjectFactory pattern

In PV, we changed the implementation of the vtkPV*Filters that have a corresponding filter in VTK-m to delegate into a vtk*Filter instead of its superclass. This allows us to create a vtkm or vtk filter using the convienient vtkObjectFactory::CreateInstance at the vtkPV*Filter at run-time.

In other words:

In the vtkPV*Filter.h

  ...
  vtk*Filter* impl;
};

In the vtkPV*Filter.cpp

vtkPV*Filter::vtkPV*Filter()
{
  // First we try to create the filter from vtkObjectFactory
  this->impl = static_cast<vtk*Filter*>(vtkObjectFactory::CreateInstance("vtk*Filter"));
  if (!this->impl)
  {
    // If it fails we default to standard vtk*Filter
    this->impl = vtk*Filter::New();
  }

Still this brings some issues as:

  1. What do we do with VTKm filters that do not have its corresponding in ParaView? A simple approach is to remove its “vtkm” prefix (Ex: vtkmExternalFaces → ExternalFaces).
  2. Where would be a good place to place a button/switch to toggle “using vtkm as a backend for filters”.

Also, I wonder how sound is the strategy that we came up, we are really looking forward feedback on original ideas to achieve our goals.

Vicente

One thing to note is that change shouldn’t prevent plugins already using factory mechanism to keep working. There is no reason it would do that, but to keep in mind.

Should this factory implementation be handled in the VTK layer instead? Perhaps the New method of the vtk filter could instantiate itself with the object factory.

vtkm*Filter vtk*Filter::New()
{
  vtkObject* obj = static_cast<vtk*Filter>(vtkObjectFactory::CreateInstance("vtk*Filter"));
  if (obj)
  {
    return static_cast<vtk*Filter>(obj);
  }
  else
  {
    return vtk*Filter::New();
  }
}

That way, the problem would be solved for other VTK applications, not just ParaView.

I guess the right way might be to create an abstract superclass shared among the “serial” and “VTK-m” versions. I’m not sure how necessary that is.

1 Like

Do the VTK-m filters have the same results as the VTK filter it represents? What if a user wants vtkmContour, but vtkOtherVtkmFilter in the same pipeline (say, due to memory limitations on their GPU if both are active at the same time)?

In principal, yes, the VTK-m filter should have the same results as the VTK filter. It should be transparent to the user. Of course, the two outputs might not be bitwise-equal. A contour could, for example, easily have the polygons in a different order or different decisions for ambiguous cases. But such changes can happen from one version of ParaView to the next, so I don’t think that would be an issue.

OK, just making sure :slight_smile: .

Should this factory implementation be handled in the VTK layer instead? Perhaps the New method of the vtk filter could instantiate itself with the object factory.

I really entertained this idea and I spent a good time figuring out how to approach this, I came up with the following scheme onto how to implement it:

  • Note that thanks to the vtkFactories vtk*FilterExtended does not depend onto vtkm*Filter at the implementation level,.

  • Also, instead of a Abstract class I added a subclass where this switch is done. This is mainly to avoid modifying the VTK filters hierarchy. It is basically a Decorator with vtkFactories

While this will indeed benefit other VTK applications, this approach requires to:

  • On ParaView, vtkPVFilters to subclass on this new vtkFiltersExtended (Not a big deal)
  • While the vtkFilterExtended does not include the vtkmFilter. At the module level, we do have to include as dependency the VTKm module in order for this whole factory thing to work (AFAIK).

I am hesitant to add those changes for VTK mainly since it will add a hard dependency onto VTKm.

I was actually thinking something more like this:

(pdf version)

You could rename the current filter (let’s call it vtkFooFilter so we don’t have wildcards) to, say, vtkFooFilterSerial (or something else appropriate). Then, create a new class with the original name (i.e. vtkFooFilter). This class has the same interface as the original filter, but it is an abstract class.

Per the rules of abstract classes in VTK, you should be able to call the New method, and the New method will use the vtkObjectFactory to create a concrete class. By default, the vtkFooFilterSerial will be registered with the vtkObjectFactory. However, if VTK-m is compiled, vtkmFooFilter can be registered instead.

Note that this means that any existing code that currently uses vtkFooFilter will continue to work as before. If VTK-m is not installed, when they call vtkFooFilter::New(), it will get vtkFooFilterSerial, which is the exact same implementation as before. However, if VTK-m is compiled, the code will get and use vtkmFooFilter.

Also note that this is exactly how the object factory is supposed to work.

The PV version of the filter (assuming we need a special PV version of the filter at all) simply needs to get a vtkFooFilter as before.

Note that the library needs linked to (and passed to vtk_module_autoinit) to work for compiled code (in general). Python will need to import the appropriate VTKm module in order to guarantee the factory implementation is registered. Note that where it is imported/instantiated doesn’t really matter, but it does need to happen.

This looks like a very sound design, however, my main concern is that in this design, In the module corresponding to vtkFooFilter (vtk.module) , don’t we have to add vtkm as a dependency? Because the New() method will be defined there? I am not 100% sure though.

Note that, I already understood this idea in the first comment and I shared it, but I tweaked it to accommodate this concern in the previous comment

This is actually somewhat similar to how it is currently implemented. The VTKm filters all inherit from the serial implementation so that they can call the superclass if the VTKm version fails for some reason. Using the factory method in the superclass and registering the VTKm subclasses, you would get the same end result. Ken’s design is cleaner on one hand but does not allow for delegating to superclass like the current implementation.

Yes, the delegation is an issue I didn’t address. I figured the VTK-m subclass would have a “has-a” link to the VTK subclass for the fallback. I didn’t put that in the diagram to hopefully make it less confusing.