Views And Representations

From ParaQ Wiki
Revision as of 12:36, 14 October 2010 by Utkarsh (talk | contribs)
Jump to navigationJump to search

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.