I’m making progress on removing smtk::model::Tessellation in favor of a class that will provide VTK (and/or VTK-m) data on demand. I would like some feedback before I go too much further.
Instead of each Component having a Tessellation object, now each Resource may (if the Resource has any renderable geometry) supply a non-null GeometryProvider. You can ask the provider about the Resource or any Component. In particular you can ask for:
a. the geometry’s serial number - like a generation number that is incremented each time the geometry changes. This must be a fast query.
b. the object’s bounds – as a 6-tuple; this is assumed to be fast.
c. the geometry’s role – which specifies how the object is to be treated by the renderer (as normal tessellation, as instance placements, as image/volume data, possibly with labels to be volume rendered). The role lets us decide which blocks inside the vtkResourceMultiBlockSource's output to place the provided data:
d. data (as a vtkDataObject*) – specifying the object’s geometry. This operation is assumed to be slow and may copy data (but may be fast using zero-copy).
The GeometryProvider must also provide a visit() method to traverse all objects that have geometry (whether they have been converted to a vtkDataObject yet or not). Traversing would not cause conversion, just report objects. This may be implemented by simply mirroring the resource’s component-visitor but some providers might be wish to be more intelligent.
Using smtk::common::Generator<ResourceSubclass, GeometryProviderPtr>, it is possible for Resource classes in the smtkCore to construct geometry providers present in other libraries (as long as the application is linked to them or SMTK has loaded a plugin containing them). Note that this means if smtkCore is loaded with no other libraries, classes like smtk::mesh::Resource will report no renderable geometry. Operations that run on resources using their native modeling kernel should work fine, but any that assume the resource’s components have a Tessellation will not work. I have not found any existing operations which do this, but it will mean some unit tests need a rewrite.
Because all resources may provide geometry, we can eliminate the subclasses of vtkResourceMultiBlockSource. And vtkResourceMultiBlockSource will just ask the resource for its geometry provider and visit all objects with geometry, only calling GeometryProvider::data() on objects with stale or non-existent cache entries.
Open Questions
@tj.corona I’m not really familiar with smtk::mesh::Resource and have a question: will a MeshSet's elements ever change? If so, can I tell when? It seems like this is possible if someone (a) modifies point coordinates or (b) adds/removes handles from the MeshSet. But I do not know how to detect this so that I can increment the relevant MeshSets’ (and only the relevant MeshSets') serial numbers. It feels like we should avoid forcing operations to query for a GeometryProvider in order to mark entries in its cache of vtkDataObjects dirty. Should the GeometryProvider be observing operation::Managers? That seems restrictive and then I’m not sure how the provider could discover the managers on its own… the application would need to configure every relevant geometry provider. Bleh! This means Python scripts, too. Double-bleh! Maybe I am changing my mind about forcing operations to do some of the work. Opinions? Or am I an ignoramus who doesn’t know how mesh can track changes?
Is requiring geometry providers to implement visit() premature optimization?
There are some special cases with roles I’m not sure exactly how to handle. Even if I don’t implement anything to cover these cases immediately, I’d like to make sure we’re not making it impossible to deal with them:
a. Some components may share geometry but use it differently.
i. Example: If we ever deal with model face-uses instead of model faces, the uses may share geometry but want to specify whether they apply to the front-face or the back-face.
ii. Example: For medical applications, geometry is usually a volumetric image. Multiple tissues/organs (each of which will be an SMTK model entity) may exist on the same image as either (a) distinct integer values in a single scalar field or (b) a separate scalar field for each tissue type.
b. Some components might serve multiple roles. For example, when a model entity has its own geometry but also serves as the prototype for some instanced geometry. I plan to deal with this particular example by asking anything that reports its Role as an instance for its prototype’s geometry. But are there other cases?
One of the reasons I’m working on this now is a medical application that needs volume rendering, slice rendering, and widgets for editing segmentation label-maps. I plan to extend vtkResourceMultiBlockSource with a new top-level block holding vtkImageData objects and then make the vtkSMTKResourceRepresentation create separate actors for each image (during ProcessViewRequest()).
I want to avoid separate representations (because that would be really messy to manage) but also want end users to have control over each image’s visual style (render as slice, volume render, etc.). Is this a bad design? It feels un-ParaView-like although it could be addressed to some degree with a self-generating source proxy. On the other hand, if I make separate representations for images inside a resource, how will things like close-resource, selection (incl. highlight-on-hover), and visibility-editing in descriptive phrase views all work? That feels un-SMTK-like.
I like the concept of having an object “represent/abstract out” the tessellation. So here are my initial questions:
What is the granularity that the GeometryProvider provide? Is it at the componentItem level? For example is there a method to call to get the geometry of a specific component item?
Is the serial number referring to the entire contents of the provider or to some smaller quanta like the resource’s components?
You say the provider will keep bounds information - same question as above, will it also keep track of component’s current bounds as well?
Not sure about the need of the visit() method yet. What I think the next step would be is to show the following useCases:
Resource loaded into SMTK.
ModelBuilder requests the first render
ModelBuilder requests the second render (resource not modified)
Yes, meshets change. I’d imagine you would know in the same way that you’d know when a model’s elements change: the components that represent the meshsets are added to the “modified” result of an operation.
I think you should treat SMTK Mesh’s handles as an implementation detail, and instead you should be listening to the results of operations (“created”, “modified”, “expunged”).
I’d need to see more of the implementation before I had a feel for how this should work.
I only understood the last part of this.
And the last part of this.
I may need you to walk me through these issues before I can be helpful. Otherwise, all I have is “Triple-bleh!”
The granularity is at the component and/or the resource level (i.e., the resource may provide geometry for itself and/or its components).
They apply to the persistent object you pass (generally components). If a resource itself has geometry, the serial number should only apply to the resource’s geometry (i.e., the resource serial number will not apply to component geometry).
Yes.
My thought was that if a resource only provided geometry for itself (no components), then visit() would be much faster than iterating over components and always getting a null geometry back.
My branch does this for the VTK session.
I am debugging this; the geometry is being fetched but not rendered.
This should be a no-op; nothing will have marked the vtkResourceMultiBlockSource as modified.
This should cause a pipeline re-execution. The vtkResourceMultiBlockSource will only replace leaf-node data objects with new versions when that leaf-node’s geometry has changed. However, I am not clear on what causes the composite polydata mapper to re-upload geometry to the GPU.
Then the serial number should not be changed. This is up to the operation and/or geometry provider; the serial number provides a mechanism for this but does not guarantee people use it.
I’m not sure that model Resource subclasses will be examining operation results to update the GeometryProvider. Currently those operations that change geometry also have calls in them to compute a new tessellation. That will be replaced with something that just increments the serial number on the geometry provider for each component involved.
I don’t know enough about smtk::mesh to say how feasible this is. For example, operations that apply changes to point coordinates would now be required to iterate meshsets and determine which ones refer to points whose coordinates have been changed. Also, as I understand it, multiple meshsets can refer to the same handles; that makes subsetting is easy, but means that changing topology of one meshset will cause changes to others… is that true?
It feels like the mesh geometry provider should provide some of this rather than relying on operation results (which would make GeometryProvider only work on managed resources).
Is that enough to help you move from “triple bleh” to “opinion?”
As discussed here and in this earlier topic, the first parts of this have been merged. The change that has been merged adds new classes (in smtk/geometry and smtk/extension/vtk/source) but does not remove smtk/model/Tessellation yet.
If a resource (1) inherits smtk::geometry::Resource and (2) provides an smtk::geometry::Geometry object (via methods defined in the new resource class), then that geometric data will be used instead of any model Tessellation objects. Otherwise, model tessellation objects will be used to produce VTK data. The new classes allow resources to provide renderable geometry without the overhead of conversion to/from SMTK’s neutral format. This will improve the responsiveness of SMTK and ModelBuilder in some cases. However, there is another change that must occur before large datasets are handled smoothly in most cases: because the vtkResourceMultiBlockSource (and its subclasses) are re-run when any component in the resource has modified geometry, VTK’s composite polydata mapper must be updated to avoid re-sending unmodified geometry to the GPU.