Table of Contents

Introduction

These web pages describe motivations for, activities during and observations about a two-day "coding camp" held over the period July 27-28, 2004 in Salt Lake City, UT at the University of Utah.

Motivation

For some time, many within the Visualization community have been striving towards the rather ambitious objective of community-wide "standards" for visualization software. The vision is to have standard interfaces for visualization tools. The term "standard interface" means many things, including standard data models, standard means for moving data into, out of and between visualization tools, standard "hooks" to the outside world to ease the process of composing larger applications from smaller building blocks. Such "standards" do not currently exist in a general sense, although there are a number of individual visualization environments and toolkits that do not interoperate with one another.

Within this context, we have an interest in evaluating how well a general-purpose computational framework borne from the scientific and high performance computing communities would fare for the purposes of serving as the substrate for families of interactive visual data analysis software technologies. We wanted to have a close look at the Common Component Architecture (www.cca-forum.org). According the CCA website:

"The objective of the CCA Forum is to define a minimal set of standard interfaces that a high-performance component framework has to provide to components, and can expect from them, in order to allow disparate components to be composed together to build a running application. Such a standard will promote interoperability between components developed by different teams across different institutions."

Part of an in-depth evaluation of CCA's efficacy as a general purpose component framework infrastructure requires attempting to use it for actual visualization work. After all, only by attempting to build components using the CCA model will we be in a position to accurately judge how well CCA will or will not work as a general purpose framework for interactive, high performance visualization.

Over the course of the past 6-9 months, a student working with our group was tasked with porting a small set of components into the CCA environment. This task turned out to be extremely arduous for several reasons:

  1. First, the CCA software is an evolving activity, so it presents a moving target for users.
  2. Second, there does not exist (to the best of my knowledge) a definite and up-to-date CCA tutorial that walks you through the steps of building your own CCA components in one of the CCA frameworks. There are some examples on the website, although they are at this point somewhat dated - from late 2002, early 2003 - as of the time of this writing (August 2004). NB - In private email, someone from the CCA forum sent me a note with a link to a rather good tutorial describing steps for building a CCA component (see https://netfiles.uiuc.edu/rhasan/ncsa/cca/cca-component-howto.html). Despite the tutorial is dated 04 June 2004, it is out of date only a month later due to differences in babel that occured during the period June-July 2004.
  3. (November 17, 2004) We note that revised tutorials were presented at the ACTS Workshop (Sep 2004) and again at SC04 (Nov 2004). We have not had the opportunity to take a look at these tutorials, but the CCA guys assert this batch is much better than previous versions. See http://www.cca-forum.org/tutorials/#sources for more information about the latest/greatest.
  4. Assuming you have managed to determine what software is needed, located and installed it, there is a great deal of specific, practical knowledge required employ all constituent technologies to produce then run a working CCA component.

We felt the best way to proceed would be to have a small, well-focused meeting where the objective was to go through the process of creating a small number of CCA visualization components from scratch. As such, we organized and held a two-day "coding camp" during July 27-28 at the University of Utah in the days preceding the July 2004 CCA Forum meeting. Thankfully, two "CCA experts" agreed to sit in and provide guidance (Jim Kohl from ORNL and Steve Parker from the University of Utah). The coding camp would not have been possible without their help and commitment.

Objectives

The high-level objectives for the July coding camp were detailed in this email message. To summarize here, we wanted to:

Approach

There are two main themes in the objectives: climbing the CCA learning curve by porting some existing technology to be a CCA component and to operate in a CCA framework; measuring the performance impact on fine-grained, iterator-based data modeling tools imposed by going through a SIDL/BABEL interface.

To achieve both sets of objectives, we created, prior to the coding camp, small example suites that would be ported into the CCA environment. One set of examples consisted of three VTK-based applications, each of increasing complexity. The second set of examples uses the Field Model library as an iterator-based data model (see http://field-model.sourceforge.net/. The overall approach we're taking is to port both sets of example applications into CCA. Here's a tarball containing the original, pre-CCA code if you're interested (John Shalf wrote these examples - thanks John!).

Results

Preparing Wes's Laptop

We spent a good deal of time installing and configuring software on Wes's laptop, which is a Sony Vaio PCG-V505BXP running SuSE 9.1. Unfortunately, we did not keep careful notes of what was installed and in what order. The following list is believed to be accurate, albeit not completely inclusive:

The first example - Single-component pipeline: VTK cones.

The first example consists, in its pre-CCA form, of a simple VTK program that creates a cone object as the first stage in an execution pipeline, then connects the cone's output to the input of a "vtkPolyMapper" object, sets the vtkPolyMapper's output to a "vtkActor", then assigns the vtkActor's output to a "vtkRenderer". Some additional renderer attributes are set, like the background color, a window is created, and an event loop is entered. The most salient portion of the code for the straight VTK implementation is shown below.

The objective of the first example was to wrap this entire program up as a CCA component. The desired result was to gain experience wrapping up some random code as a CCA component and to have it run.

Example 1. - Subset of Unmodified VTK Application


int main( int argc, char *argv[] )
{
  // 
  // Next we create an instance of vtkConeSource and set some of its
  // properties. The instance of vtkConeSource "cone" is part of a
  // visualization pipeline (it is a source process object); it produces data
  // (output type is vtkPolyData) which other filters may process.
  //
  vtkConeSource *cone = vtkConeSource::New();
  cone->SetHeight( 3.0 );
  cone->SetRadius( 1.0 );
  cone->SetResolution( 10 );
  
  // 
  // In this example we terminate the pipeline with a mapper process object.
  // (Intermediate filters such as vtkShrinkPolyData could be inserted in
  // between the source and the mapper.)  We create an instance of
  // vtkPolyDataMapper to map the polygonal data into graphics primitives. We
  // connect the output of the cone souece to the input of this mapper.
  //
  vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New();
  coneMapper->SetInput( cone->GetOutput() );

  // 
  // Create an actor to represent the cone. The actor orchestrates rendering
  // of the mapper's graphics primitives. An actor also refers to properties
  // via a vtkProperty instance, and includes an internal transformation
  // matrix. We set this actor's mapper to be coneMapper which we created
  // above.
  //
  vtkActor *coneActor = vtkActor::New();
  coneActor->SetMapper( coneMapper );

  //
  // Create the Renderer and assign actors to it. A renderer is like a
  // viewport. It is part or all of a window on the screen and it is
  // responsible for drawing the actors it has.  We also set the background
  // color here.
  //
  vtkRenderer *ren1= vtkRenderer::New();
  ren1->AddActor( coneActor );
  ren1->SetBackground( 0.1, 0.2, 0.4 );

  //
  // Finally we create the render window which will show up on the screen.
  // We put our renderer into the render window using AddRenderer. We also
  // set the size to be 300 pixels by 300.
  //
  vtkRenderWindow *renWin = vtkRenderWindow::New();
  renWin->AddRenderer( ren1 );
  renWin->SetSize( 300, 300 );

  // 
  // The vtkRenderWindowInteractor class watches for events (e.g., keypress,
  // mouse) in the vtkRenderWindow. These events are translated into
  // event invocations that VTK understands (see VTK/Common/vtkCommand.h
  // for all events that VTK processes). Then observers of these VTK
  // events can process them as appropriate.
  vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
  iren->SetRenderWindow(renWin);

  //
  // By default the vtkRenderWindowInteractor instantiates an instance
  // of vtkInteractorStyle. vtkInteractorStyle translates a set of events
  // it observes into operations on the camera, actors, and/or properties
  // in the vtkRenderWindow associated with the vtkRenderWinodwInteractor. 
  // Here we specify a particular interactor style.
  vtkInteractorStyleTrackballCamera *style = 
    vtkInteractorStyleTrackballCamera::New();
  iren->SetInteractorStyle(style);

  //
  // Unlike the previous scripts where we performed some operations and then
  // exited, here we leave an event loop running. The user can use the mouse
  // and keyboard to perform the operations on the scene according to the
  // current interaction style. When the user presses the "e" key, by default
  // an ExitEvent is invoked by the vtkRenderWindowInteractor which is caught
  // and drops out of the event loop (triggered by the Start() method that
  // follows.
  //
  iren->Initialize();
  iren->Start();
  
  // 
  // Final note: recall that an observers can watch for particular events and
  // take appropriate action. Pressing "u" in the render window causes the
  // vtkRenderWindowInteractor to invoke a UserEvent. This can be caught to
  // popup a GUI, etc. So the Tcl Cone5.tcl example for an idea of how this
  // works.

  //
  // Free up any objects we created. All instances in VTK are deleted by
  // using the Delete() method.
  //
  cone->Delete();
  coneMapper->Delete();
  coneActor->Delete();
  ren1->Delete();
  renWin->Delete();
  iren->Delete();
  style->Delete();

  return 0;
}

There's not much to the resulting CCA component except for the fact that it has a "Go" button. After you launch the Ccaffeine framework and then launch the GUI, instantiate the component and press on the green Go button to launch the component. When you're finished transforming the cone, close the window.

Unfortunately, the combination of Ccaffeine and GUI are not sufficiently robust at this time to support a second run of the single component, nor does it appear that you can clear the work palette of all components and start anew. When we tried to do so on Wes's laptop, Ccaffeine appears to abnormally terminate and the GUI wedges.

(November 15, 2004). Jim Kohl makes the following observation: I wonder if the problem with Ccaffeine and the GUI tanking is due to the fact that the go() method never returns... When you run go(), the window pops up and you play for a while, but then there's no "done" button, and so you have to just kill the cone window... That's not "playing by the CCA rules" - you're supposed to return success or failure so the framework can continue executing, etc... (This is actually an interesting point about the way viz event loops would work inside a CCA framework... something we ultimately need to explore...)

Results of Example1

The image below shows the CCA GUI, along with the output of the ported example. Modulo the numerous small details you have to get right when creating a CCA component, it was straightforward to port a complete VTK program to be a single component in the CCA environment. This simple example has no inputs, outputs, nor runtime parameters that may be manipulated, so it is a contrived example intended to gain us some experience porting a self-contained example into CCA.

The second example - Two-component pipeline: VTK cones going to a VTK renderer window, interaction.

In the second example, we begin with the VTK application source code listed above, but create two components rather than one. Rather than wrap the entire application as a single CCA component, we arbitrarily chose a partition point within the application and put part of the application in one component, and the remainder in a second component.

The first component, called "ConeGen", implements the first part of the VTK pipeline where we create a cone object and assign it to a "vtkPolyMapper." The relevant code from the ConeGen component that creates the cone and attaches it to the vtkPolyMapper is as follows:

  // DO-NOT-DELETE splicer.begin(ConeGen1.ConeGen.getGeomBlob)
  // insert implementation here
  vtkConeSource *cone = vtkConeSource::New();
  cone->SetHeight( 3.0 );
  cone->SetRadius( 1.0 );
  cone->SetResolution( 10 );
  
  // 
  // In this example we terminate the pipeline with a mapper process object.
  // (Intermediate filters such as vtkShrinkPolyData could be inserted in
  // between the source and the mapper.)  We create an instance of
  // vtkPolyDataMapper to map the polygonal data into graphics primitives. We
  // connect the output of the cone souece to the input of this mapper.
  //
  vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New();
  coneMapper->SetInput( cone->GetOutput() );

  return coneMapper; // hope this works!!
  // DO-NOT-DELETE splicer.end(ConeGen1.ConeGen.getGeomBlob)

The important thing to note here is the mechanism by which the data object is provided to the next component. The ConeGen component merely returns a handle to the vtkPolyDataMapper object, which is cast to a void *. The Ccaffeine framework simply hands this pointer to the next component, which will then do something with the pointer.

The code for the "renderer component" is as follows. Note that we obtain the void * pointer using the first two lines of executable code in the code block shown below.

  // DO-NOT-DELETE splicer.begin(ConeGen1.VTKRendererSimple.go)
  // insert implementation here

  //  vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New(); old way

  // alternate to fully qualified ConeGen1::GeomBlobPort would be to
  // say "using namespace ConeGen1;" for the current scope
  ConeGen1::GeomBlobPort geomBlobPort = services.getPort("geomBlob");
  vtkPolyDataMapper *coneMapper = (vtkPolyDataMapper *)geomBlobPort.getGeomBlob();
  services.releasePort("geomBlob"); // after we're done with the port, go ahead
  // and release it.

  // 
  // Create an actor to represent the cone. The actor orchestrates rendering
  // of the mapper's graphics primitives. An actor also refers to properties
  // via a vtkProperty instance, and includes an internal transformation
  // matrix. We set this actor's mapper to be coneMapper which we created
  // above.
  //
  vtkActor *coneActor = vtkActor::New();
  coneActor->SetMapper( coneMapper );

  ... 

  (the rest of the code is omitted - it is essentially the same as
  the original VTK application.)

CCA does not provide any data models that are used to hold data, nor does it provide any infrastructure to marshal data between components.

In this particular case, we just happen to have two components that can operate on the same opaque data type, which is a VTK object. Furthermore, this aproach works in this case because both components are in the same address space. In other words, the Ccaffeine "implementation" of the two components is no more than what you would get if you were linking together a couple of subroutines. It is less than what you would get doing a full compile-link step as there would be some type checking done to verify that the "subroutine arguments" are of the correct type. In this case, CCA is letting us pass a void * between components, so there is no real guarantee of anything other than a pointer being handed between components.

The third example - Two-component pipeline, using an FLTK slider on to update the parameters to a VTK cone generator that in turn drives a VTK renderer wrapped with FLTK.

In the third example, we build upon the two-component model in the second example. Like the second example, we have two components: one generates a cone, and passes its data via a void * to the next component, which performs rendering. To this basic model we add two bits of capability to further test out how far we can exercise a use pattern typical of interactive visualization applications. First, we combine a GUI toolkit with the rendering infrastructure, so the previous VTK renderer is now augmented by FLTK, which provides a "quit button" as well as a slider. Second, the slider is used to provide an integer that we want to use as a parameter for the cone-generating component. This model corresponds to having a dial that is used to control an isocontouring level, which in turn generates new geometry that is then passed along to a rendering component.

As with the second example, the code fragment below shows how we create the cone object using VTK objects. When we're done constructing the cone, we pass along the geometry to the outside world by handing off a void * to the vtkPolyMapper object. What is different in this chunk of code is that we're also passing in a handle to the vtkRenderer object from the rendering component. The reason we have to do this is because we want to signal the renderer when it has new goemetry to draw. In other words, we are manually directing execution of a VTK execution pipeline that is spread across two components. One part of the pipeline is concerned with updating new scene data, the other tells the renderer to use and draw the new scene data.

void*
ConeGen2::ConeGen_impl::getGeomBlob (
  /*in*/ ::ConeGen2::RedrawInterface redrawer ) 
throw () 
{
  // DO-NOT-DELETE splicer.begin(ConeGen2.ConeGen.getGeomBlob)
  // insert implementation here
  cone = vtkConeSource::New();
  cone->SetHeight( 3.0 );
  cone->SetRadius( 1.0 );
  cone->SetResolution( resolutionValue );

  // Tuck away redrawer for subsequent change events
  renderer = redrawer;
  
  // In this example we terminate the pipeline with a mapper process object.
  // (Intermediate filters such as vtkShrinkPolyData could be inserted in
  // between the source and the mapper.)  We create an instance of
  // vtkPolyDataMapper to map the polygonal data into graphics primitives. We
  // connect the output of the cone souece to the input of this mapper.
  //
  vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New();
  coneMapper->SetInput( cone->GetOutput() );

  return coneMapper; // hope this works!!  It'll work...
  // DO-NOT-DELETE splicer.end(ConeGen2.ConeGen.getGeomBlob)
}

Also in the first component is some code to do something with new input integer (parameter) values. The code fragment below shows that we take the input integer value, stick it into a private variable "resolutionValue," update the cone geometry, and then tell the renderer to redraw.

void
ConeGen2::ConeGen_impl::setValue (
  /*in*/ int32_t value ) 
throw () 
{
  // DO-NOT-DELETE splicer.begin(ConeGen2.ConeGen.setValue)
  // insert implementation here
  resolutionValue = value;
  if(cone){
    cone->SetResolution(value);
    cone->Modified();
    if(renderer._not_nil())
      renderer.redraw();
  }
  // DO-NOT-DELETE splicer.end(ConeGen2.ConeGen.setValue)
}

On the rendering component side, the component consumes the geometry by being handed a void * as in Example 2. The addition is the use of FLTK to add a GUI layer. More importantly, we need to convey the slider data to the first component whenever the user changes the slider value. The following code fragment is the callback routine attached to the slider. It grabs the integer value from the slider via "slider->value()", and the value is passed as an opaque thing to the "slider" port on the component that generates geometry.

void slide_cb(Fl_Slider *slider,MyPipeline *p){
  ConeGen2::IntegerParameterPort paramPort = p->services.getPort("slider");
  paramPort.setValue((int)slider->value());
  p->services.releasePort("slider");
  p->fl_vtk_window->redraw();
  p->ren->Render();

Code tarball of these examples.

CCA-July04.tgz (32MB). Here's the README file from the tarball describing its contents and giving some hints about how to port to your environment.

Discussion

We learned a number of things from the exercise of porting a couple of simple examples to be CCA components.

What Next?

We are going to dig deeper into the distributed computing and performance measurement questions at the next DiVA/CCA coding camp, which will be held August 25-26 2004 at a TBD location (somewhere in Berkeley, CA).

(November 17, 2004) Jim points out that in the time between the July coding camp and now, there are efforts underway in CCA to streamline the build process and associated configuration files as part of the CCA toolkit effort. We have not looked at the recent SC04 CCA tutorial examples to make an assessment about how much has changed since July 2004. Note also that our coding camp used Babel 0.9.2, and the current version is 0.9.8. We do not expect that our examples will work smoothly with the current version of Babel. If time permits, we will update our examples so that we're sure they work with a current version of Babel.