Views And Representations
UNDER CONSTRUCTION
Background
Views and their consorts, representations, have been the most complex components of the ParaView visualization pipeline. This wasn't without merit, ParaView rendering framework is indeed quite flexible and powerful, working without issues (for the most parts :) ) in different configurations and customizations.
The concept of Views and Representations is pretty simple really. A View is an abstraction that can "show" data, and Representation is an abstraction that can present/format the raw data so that it can be "shown" in the view. How the data is shown? Is it rendered as polygons in an OpenGL window or as a list of values in a QTableWidget are immaterial. At the end of any visualization pipeline, you have data that needs to be "shown" and representations and views do that for you.
This notion was found so useful that we now have views and representations in VTK itself. vtkDataRepresentation is a vtkAlgorithm subclass, a sink to be more precise. One simply connects a data source to the representation's input and dumps it in a view and voila! You have a display for your data.
vtkView, vtkDataRepresentation and their subclasses have been maturing over the past year or so in VTK repository. ParaView meanwhile has its own ServerManager level view-representation infrastructure which developed parallel to VTK's and has similar overtones but with the added backbone to support parallel rendering and client-server operations.
Besides being parallel VTK's views and representations, the main difficulty with ParaView's views/representations has been its complexity and poor readability. This was due to the large number of components involved as well as the fact that coding complex pipelines in ServerManager is much harder to debug and follow when compared with the same pipeline in VTK level.
So we decided to add new VTK-based views and representations that could leverage ParaView's parallel infrastructure for interprocess communication without adding complexity in the ServerManager level.
VTK-based Views And Representations
In brief, vtkDataRepresentation is vtkAlgorithm with no output ports. In RequestData() stage, sets up the internal rendering pipeline (typically mapper/actor). In AddToView() it adds the actor to the View's renderer. And that sums it up. The view before every render calls Update() on all representations and then triggers a render -- plain and simple. This breaks down for complex views like the RenderView which supports rendering on different processes. We still can use this single pass setup for client-only views or view that do rendering at a fixed location e.g. spread-sheet views/chart views.
If only life was easy!
As just described, all the view does is update the representation and then render. Now this works well when you actually have data pipelines (or trivially when the representation doesn't need any input). Now in ParaView world, expecting the data to be present is not a legitimate expectation. The data pipelines are only on the data-server, while the rendering components need to render either on the data-server itself, or render-server or client (based on configuration and settings). So if we assume that the vtkDataRepresentation is instantiated on all processes, then it will have valid input on some and nothing connected to the input on other nodes. A representation is responsible for converting the data to representable form. Thus a surface-representation will possibly convert an unstructured grid to polydata shell so that it can be rendered. Now this can only be done in RequestData(). The view may need to know the geometry sizes to decide where to render on server or client, for example. The view should now make that decision and send it to all representations so that they can then deliver the geometry to the right node where rendering is going to happen. Plus there's ordered compositing, which may require the geometry to be redistributed. This explanation hopefully makes it clear that we need more passes for the representations after the traditional pipeline passes, and we need mechanism to communicate various parameters from views to representations such as the rendering-location decision.
vtkPVView
Before we look at the complicated case of writing representations for render view, just try to understand the core base classes vtkPVView and vtkPVDataRepresentation.
vtkPVView is a vtkView subclass that adds some ParaView specific API to the view. vtkPVView object is expected to be instantiated on all processes. In your subclass you can have smarts to do the actual rendering on the appropriate nodes.
Every vtkPVView has a Position and Size. This is essential for multi-view configuration to work. pqQVTKWidget has code to ensure that the ViewPosition and ViewSize properties are updated correctly whenever the widget's position/size changes.
vtkPVView has a instance of vtkPVSynchronizedRenderWindows. This class encapsulates the logic to synchronize render-windows among multiple processes as well as propagate render event from the client to all the processes. If your view needs a render window, it should use vtkPVSynchronizedRenderWindows::NewRenderWindow() to obtain the render window to use. vtkPVSynchronizedRenderWindows::NewRenderWindow() will return a new vtkRenderWindow instance on the client while on the server it will reuse the same renderwindow among all views, but internally ensure that the viewports for the renderers for each view are setup correctly so that they don't overlap.
If vtkPVView::Initialize, one should register the window and the renderers with the vtkPVSynchronizedRenderWindows using the id passed to the Initialize() function. Every view must have a unique id and the same id on all processes. This is ensured by vtkSMViewProxy.
vtkPVDataRepresentation
vtkPVDataRepresentation is a vtkDataRepresentation subclass that adds some ParaView specific API to the representation.
vtkPVDataRepresentation has the following responsibilities:
- Prepare data for rendering e.g. use a geometry filter to extract the surface
- Delivery data to rendering node e.g. in case of chart views, it delivers the
data to the client.
- Map the data to rendering primitives if applicable e.g. use a
vtkPolyDataMapper/vtkActor to render geometry in the render window.
Some of the important API of vtkPVDataRepresentation is as follows:
// Description:
// This is called by view for processing view rendering passes. These passes
// may include requesting a vtkAlgorithm::Update() or other render-view
// specific requests such as getting geometry information etc. Subclasses
// typically check if their own visibility before responding to any view
// requests thus avoid any representation updates when the representation is
// not visible.
virtual int ProcessViewRequest(vtkInformationRequestKey* request_type,
vtkInformation* inInfo, vtkInformation* outInfo);
// Description:
// This is one of the most important functions. In VTK pipelines, it's very
// easy for the pipeline to decide when it needs to reexecute.
// vtkAlgorithm::Update() can go up the entire pipeline to see if any filters
// MTime changed (among other things) and if so, it can rexecute the pipeline.
// However in case of representations, the real input connection may only be
// present on the data-server nodes. In that case the
// vtkPVDataRepresentation::RequestData() will only get called on the
// data-server nodes. That means that representations won't be able to any
// data-delivery in RequestData(). We'd need some other mechanisms to
// synchronize data-delivery among processes. To avoid that conumdrum, the
// vtkSMRepresentationProxy calls MarkModified() on all processes whenever any
// filter in the pipeline is modified. In this method, the
// vtkPVDataRepresentation subclasses should ensure that they mark all
// delivery related filters dirty to ensure they execute then next time they
// are updated. The vtkPVDataRepresentation also uses a special executive
// which avoids updating the representation unless MarkModified() was called
// since the last Update(), thus acting as a update-suppressor.
// If the subclass has any method that results in changes that require the
// data to be redelivered, it should call MarkModified() rather than simply
// Modified().
virtual void MarkModified();
vtkPVRenderView
vtkPVRenderView is vtkPVView subclass that is used as the default view for 3D rendering. This may be the most complicated ParaView view.
Some peculiarities of this view are as follows:
- It supports rendering on different processes (client or render-server nodes)
- It support tile-display configuration
- It supports ordered compositing when needed for volume rendering or rendering
translucent geometry.
- It has 2 renderer, a rendered which is composited and a non-composited
renderer that is typically used to render overlay props such as scalar bars.
During construction, vtkPVRenderView uses the vtkPVSynchronizedRenderWindows to obtain a render window for the processes. It adds the composited and non-composited renderers to this render window.
During rendering the render view uses multiple passes to communicate with the representations. These are described next.
UPDATE PASS: vtkAlgorithm passes and RequestData
First pass is called vtkAlgorithm::Update() on the representations. Thus results in all the traditional algorithm passes. In RequestUpdateExtent() the representation should setup piece/time request. UpdateTime, CacheKey, UseCache flags are passed from the view to the representation before calling Update().
In RequestData() the representation is expected to prepare data for rendering i.e. apply geometry filter or do any other preprocessing that needs to be done every time the data changes.
REQUEST_INFORMATION: Provide information to the View
In this pass the representation provides the view with the information about geometry_size that the view uses to make decision on rendering location for the current render based on the user-settings.
If ordered compositing is needed, then the view needs the rendered data to build the KdTree. To enable that, this pass should also convey to the view the filter that delivers the data to the rendering nodes (look at vtkGeometryRepresentation::GenerateMetaData). This is only done by the vtkGeometryRepresentation and vtkUnstructuredGridVolumeRepresentation i.e. representations that support redistribution for ordered compositing. Other representation don't really care.
REQUEST_PREPARE_FOR_RENDER: Deliver data to rendering nodes
In this pass, the view conveys the rendering-location decision to the representation and the representation then delivers the geometry to the appropriate nodes. Actually, this isn't as bad as it sounds. We have a plethora of custom filters e.g. vtkUnstructuredDataDeliveryFilter, vtkSelectionDeliveryFilter, vtkImageSliceDataDeliveryFilter, that make it easier to automatically delivery the data to of the correct type to the right rendering node.
REQUEST_RENDER: Render
For most representations, this pass is not of much interest. The view passes the KdTree generated if any, for ordered compositing in this pass to the represenation. Only vtkGeometryRepresentation and vtkUnstructuredGridVolumeRepresentation use this KdTree to redistribute the geometry among the rendering nodes.
Summary of Views and Representations
Here's a summary of the classes for views and representations and their function.
View Class Name | Superclass | Function |
---|---|---|
vtkPVView | vtkView | Base class for all ParaView Views. Defines ParaView specific API additions to
the vtkView class. All ParaView views must inherit from this class. It handles layout of windows/viewports in multiple view configurations. |
vtkPVRenderView | vtkPVView | Default render view for ParaView supporting 3D rendering of datasets. Support
all the different configurations that ParaView can run in i.e. builtin, client-server, batch, tile-display etc. |
vtkPV2DRenderView | vtkPVRenderView | vtkPVRenderView subclass that changes the interactor style to support only 2D
interactions. Used for the 2D View in ParaView. |
vtkPVContextView | vtkPVView | View used for charts i.e. view using vtkContextView such as Parallel
coordinates. |
vtkPVXYChartView | vtkPVContextView | vtkPVContextView customization for XY-line and XY-bar charts. Adds some API
specific to these classes. |
Representation Class Name | Superclass | Function |
---|---|---|
vtkPVDataRepresentation | vtkDataRepresentation | Base class for all ParaView representations. Adds some ParaView specific API
to vtkDataRepresentation. It's possible to directly use vtkDataRepresentation representation and subclasses in ParaView for representations that don't have any inputs e.g. 3D widgets. |
vtkCompositeRepresentation | vtkPVDataRepresentation | vtkCompositeRepresentation makes it possible to create composite
representations using other representations with 1 representation active at a time. |
vtkPVCompositeRepresentation | vtkCompositeRepresentation | ParaView uses a composite representation to show data in the 3D view. This
makes it possible for user to pick among different types of representations. vtkPVCompositeRepresentation is the class that manages that. It also has internal representations for showing the selection and cube-axes. |
vtk3DWidgetRepresentation | vtkDataRepresentation | Adds support for 3D widgets and representations. |
vtkDataLabelRepresentation | vtkPVDataRepresentation | Representation for labelling cells and points of the input. It merely clones
the input data on all nodes so be wary of that when using this to label really large datasets. |
vtkGeometryRepresentation | vtkPVDataRepresentation | Representation used to show surface/points/wirframe for any dataset in 3D
View. This supports all ordered compositing and is the default representation for showing geometries in ParaView's 3D view. |
vtkGlyph3DRepresentation | vtkGeometryRepresentation | vtkGeometryRepresentation subclass that uses a special mapper for rendering
glyphs. Since it extends vtkGeometryRepresentation it has all the perks of the vtkGeometryRepresentation such as support for ordered compositing. |
vtkImageVolumeRepresentation | vtkPVDataRepresentation | Representation for volume rendering image datasets. This can only render in
built-in and pvserver or data-server mode with remote rendering. We don't support moving image volumes around hence in those configurations this only renders an outline. |
vtkImageSliceRepresentation | vtkPVDataRepresentation | Representation for showing a single slice from an image data. |
vtkSelectionRepresentation | vtkPVDataRepresentation | Representation for showing the geometry as well as labels (uses
vtkGeometryRepresentation and vtkDataLabelRepresentation) internally. Although it's called vtkSelectionRepresentation it doesn't not by default show the extracted selection output or anything. It's hooked up by vtkPVCompositeRepresentation to show the extracted selection. |
vtkUnstructuredGridVolumeRepresentation | vtkPVDataRepresentation | Representation for volume rendering of unstructured datasets. |
vtkTextSourceRepresentation | vtkPVDataRepresentation | vtkTextSourceRepresentation is a representation to show text. The input is
expected to a vtkTable with a single row and column (atleast on the data server nodes). The content of this entry in the table is shown as text on the rendering nodes. |
vtkSpreadSheetRepresentation | vtkPVDataRepresentation | Representation used by vtkSpreadSheetView. |
vtkChartRepresentation | vtkPVDataRepresentation | Base class for all charting representations. |
vtkXYChartRepresentation | vtkChartRepresentation | Representation for XY line and bar charts. |
vtkParallelCoordinatesRepresentation | vtkChartRepresentation | Representation for parallel coordinates view. |
vtkCubeAxesRepresentation | vtkPVDataRepresentation | Representation to show a cube-axes using the input data bounds. Used
internally by vtkPVCompositeRepresentation. |
vtkOutlineRepresentation | vtkGeometryRepresentation | Shows merely the outline for the input dataset. |