VTK/Java Wrapping

From KitwarePublic
Jump to navigationJump to search

Configuration

You basically just need to turn VTK_WRAP_JAVA on in CMake and build.

Bartlomiej Wilkowski has created a nice tutorial of configuring Java wrapping with VTK.

Windows

To run a sample application provided in VTK against your VTK build directory (with an installed VTK remove "Debug"):

$ set PATH=%PATH%;your_vtk_build_dir\bin\Debug
$ java -cp your_vtk_build_dir\bin\vtk.jar vtk.sample.Demo

Mac

To run a sample application provided in VTK against your VTK build directory (with an installed VTK replace "bin" with "lib"):

$ export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:your_vtk_build_dir/bin
$ java -cp your_vtk_build_dir/bin/vtk.jar vtk.sample.Demo

Linux

To run a sample application provided in VTK against your VTK build directory (with an installed VTK replace "bin" with "lib"):

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:your_vtk_build_dir/bin
$ java -cp your_vtk_build_dir/bin/vtk.jar vtk.sample.Demo

Sample Code (from VTK/Wrapping/Java/vtk/sample/SimpleVTK.java)

<source lang="java"> /**

* An application that displays a 3D cone. A button allows you to close the
* application.
*/

public class SimpleVTK extends JPanel implements ActionListener {

   private static final long serialVersionUID = 1L;
   private vtkPanel renWin;
   private JButton exitButton;
   // -----------------------------------------------------------------
   // Load VTK library and print which library was not properly loaded
   static {
       if (!vtkNativeLibrary.LoadAllNativeLibraries()) {
           for (vtkNativeLibrary lib : vtkNativeLibrary.values()) {
               if (!lib.IsLoaded()) {
                   System.out.println(lib.GetLibraryName() + " not loaded");
               }
           }
       }
       vtkNativeLibrary.DisableOutputWindow(null);
   }
   // -----------------------------------------------------------------
   public SimpleVTK() {
       super(new BorderLayout());
       // build VTK Pipeline
       vtkConeSource cone = new vtkConeSource();
       cone.SetResolution(8);
       vtkPolyDataMapper coneMapper = new vtkPolyDataMapper();
       coneMapper.SetInputConnection(cone.GetOutputPort());
       vtkActor coneActor = new vtkActor();
       coneActor.SetMapper(coneMapper);
       renWin = new vtkPanel();
       renWin.GetRenderer().AddActor(coneActor);
       // Add Java UI components
       exitButton = new JButton("Exit");
       exitButton.addActionListener(this);
       add(renWin, BorderLayout.CENTER);
       add(exitButton, BorderLayout.SOUTH);
   }
   /** An ActionListener that listens to the button. */
   public void actionPerformed(ActionEvent e) {
       if (e.getSource().equals(exitButton)) {
           System.exit(0);
       }
   }
   public static void main(String s[]) {
       SwingUtilities.invokeLater(new Runnable() {
           @Override
           public void run() {
               JFrame frame = new JFrame("SimpleVTK");
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.getContentPane().setLayout(new BorderLayout());
               frame.getContentPane().add(new SimpleVTK(), BorderLayout.CENTER);
               frame.setSize(400, 400);
               frame.setLocationRelativeTo(null);
               frame.setVisible(true);
           }
       });
   }

} </source>

Some key points from this code to note:

  • vtkNativeLibrary.LoadAllNativeLibraries() is required to load the dynamic libraries from VTK's C++ core. This call must happen before any VTK code executes, which is why it is put in a static block in our application class.
  • vtkNativeLibrary.DisableOutputWindow(null) simply hides any debugging information that may otherwise pop up in a dialog box when any warnings are reached. This is good to call when releasing an application.
  • SwingUtilities.invokeLater(...) is called because technically all GUI code, including setting up and using a VTK render window, should happen in the Swing event thread.

Threading Sample Code (from VTK/Wrapping/Java/vtk/sample/Demo.java)

In this demo, we want to illustrate the correct way to perform VTK tasks on separate threads in Java. The first thing to note is that VTK is inherently NOT thread-safe, which immediately rules out several possible use cases. Calling methods on the same VTK objects across threads, even if they seem to be read-only, should be avoided. The safest approach is to "hand off" objects from one thread to another, so one thread is completely done with an object before another thread begins manipulating it. Reclaiming memory for VTK objects is particularly tricky to perform across threads, as deleting a single VTK object may potentially cause the entirety of VTK objects to be modified. While we expose the Delete() method to explicitly delete VTK objects, if you are using VTK objects in multiple threads this is discouraged unless you are aware of its potential issues. VTK provides a special garbage collector for VTK objects in the Java layer that may be run manually or automatically at intervals if memory reclaiming is needed.

For this example, we will have a checkbox for turning on and off the VTK garbage collection while an application is running. The application creates new actors using a separate processing thread, which are then added dynamically to the VTK renderer. This enables data to be loaded and processed without causing lags in the frame rate of the interactive 3D view.

We need to implement a worker that is capable of producing actors. In the sample code we produce sphere actors with shrunk polygons in order to have something interesting that takes a bit of time to create. These will execute on separate threads to keep the rendering interactive. <source lang="java">

   public static class PipelineBuilder implements Callable<vtkActor> {
       private vtkActor actor;
       ...
       @Override
       public vtkActor call() throws Exception {
           // Set up a new actor
           actor = new vtkActor();
           ...
           // Wait some time for other thread to work
           Thread.sleep((long) (Math.random() * 500));
           // Return
           return actor;
       }
   }

</source>

A separate worker's job is to add actors to the renderer when ready. <source lang="java">

   public static class AddActorRunnable implements Runnable {
       private vtkActor actorToAdd;
       private vtkRenderer renderer;
       private vtkPanel panel;
       void setRenderer(vtkPanel panel) {
           this.renderer = panel.GetRenderer();
           this.panel = panel;
       }
       void setActor(vtkActor a) {
           this.actorToAdd = a;
       }
       @Override
       public void run() {
           this.renderer.AddActor(this.actorToAdd);
           this.panel.Render();
       }
   }

</source>

In our initialization code, we need to set up several things. First, two checkboxes toggle VTK's garbage collection and the debug mode. Since VTK is a C++ library, it has its own mechanism for ensuring that unused objects are deleted from memory. Many threading issues can be avoided by simply turning off VTK garbage collection.

<source lang="java">

       runGC = new JCheckBox("Enable GC", false);
       debugMode = new JCheckBox("Debug mode", false);

</source>

We need to set up our completion service. <source lang="java">

       exec = new ExecutorCompletionService<vtkActor>(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));

</source>

Next a setupWorkers() method starts a thread which invokes the code to add actors to the renderer whenever our executor completion service has a new actor available. Note that the code adding the actors to the renderer must be done on the event thread using SwingUtilities.invokeAndWait(), since that is where the renderer object was created and lives. <source lang="java">

   private void setupWorkers() {
       // Add actor thread: Consume the working queue and add the actor into
       // the render inside the EDT thread
       final AddActorRunnable adderRunnable = new AddActorRunnable();
       adderRunnable.setRenderer(panel3d);
       new Thread() {
           public void run() {
               for (int i = 0; i < NUMBER_OF_PIPLINE_TO_BUILD; i++) {
                   try {
                       adderRunnable.setActor(exec.take().get());
                       SwingUtilities.invokeAndWait(adderRunnable);
                       panel3d.repaint();
                   } catch (InterruptedException e) {
                       return;
                   } catch (ExecutionException e) {
                       e.printStackTrace();
                   } catch (InvocationTargetException e) {
                       e.printStackTrace();
                   }
               }
           };
       }.start();
   }

</source>

To load the completion service with jobs to run on a pool of threads in the background, we submit a collection of PipelineBuilder objects to be executed at a later time.

<source lang="java">

   public void startWorking() {
       for (int i = 0; i < NUMBER_OF_PIPLINE_TO_BUILD; i++) {
           exec.submit(new PipelineBuilder());
       }
   }

</source>

We'll also create a timer which every second renders the scene and takes out a sphere actor. This code also manually run the garbage collector using vtkObject.JAVA_OBJECT_MANAGER.gc() if the runGC checkbox is selected. Note that timers are scheduled on the Swing event thread, which is why we are allowed to manipulate the renderer and its actors here. We call the garbage collector manually in order to update the UI with garbage collector information.

<source lang="java">

       // Update GC info into the UI every second.
       // Reset camera each of the first 10 seconds.
       this.nbSeconds = 0;
       new Timer(1000, new ActionListener() {
           @Override
           public void actionPerformed(ActionEvent e) {
               if (nbSeconds++ < 10) {
                   panel3d.resetCamera();
               }
               vtkRenderer renderer = panel3d.GetRenderer();
               if (renderer.GetNumberOfPropsRendered() > 1) {
                   renderer.RemoveActor(renderer.GetActors().GetLastProp());
               }
               // Run GC in local thread (EDT)
               if (runGC.isSelected()) {
                   vtkReferenceInformation info = vtkObject.JAVA_OBJECT_MANAGER.gc(debugMode.isSelected());
                   if (debugMode.isSelected()) {
                       System.out.println(info.listKeptReferenceToString());
                       System.out.println(info.listRemovedReferenceToString());
                   }
                   gcStatus.setText(info.toString());
               } else {
                   gcStatus.setText("");
               }
               panel3d.Render();
           }
       }).start();

</source>

Instead of manually running the garbage collector, we can set up automatic garbage collection using a global scheduler. Turn on automatic garbage collection with the following statement. It is important to note that by default it is off, so be sure to include this line to reclaim memory from the VTK layer.

<source lang="java">

       vtkObject.JAVA_OBJECT_MANAGER.getAutoGarbageCollector().SetAutoGarbageCollection(true);

</source>

To set up the interval at which collection runs, use SetScheduleTime. In this case it will run garbage collection every second. The automatic garbage collector runs in the event thread by default.

<source lang="java">

       vtkObject.JAVA_OBJECT_MANAGER.getAutoGarbageCollector().SetScheduleTime(1, TimeUnit.SECONDS);

</source>

Another option will collect statistics on the garbage collector, that can be retrieved by listKeptReferenceToString() and listRemovedReferenceToString() on the collector's information object returned from the gc() method.

<source lang="java">

       vtkObject.JAVA_OBJECT_MANAGER.getAutoGarbageCollector().SetDebug(true);

</source>

Java Wrapper Refactoring (Oct 8, 2007)

There were a few problems with the old Java wrappers. One was that, as you said, objects were being deleted before they were supposed to. We hacked in a fix at one point about a year ago which basically made all VTK objects accessed from Java stay around forever, but this was not acceptable either.

Ref:

The other major concern was that the map from Java objects to VTK objects was in the C++ JNI layer, and while we tried to keep this map synchronized with a mutex, race conditions could still occur because other Java threads could advance while the JNI layer was being called (a thread could access a C++ object just as it is being garbage-collected and deleted). There does not seem to be a way to atomically call a JNI method, or ensure the collector doesn't run while a method is called. This second issue forced us to rethink how the map is done, and the solution was to keep the map in Java instead of C++. But we didn't want this Java map to prohibit objects from being garbage collected. Fortunately, Java has a WeakReference class for just this type of purpose. When accessed, the reference will either be valid or null depending on whether it has been garbage-collected.

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ref/WeakReference.html

Thus, the wrapper code can lookup objects in this map when returning objects from methods, and if it is not there, or null, it creates a new Java object representing that C++ object.

A final issue was that we wanted a way to guarantee all C++ destructors are called before the program exits. The natural place to decrement the reference count of the C++ object is in finalize(), which works when things are garbage-collected, but Java does not guarantee that finalize will ever be called. So the method vtkGlobalJavaHash.DeleteAll() will plow through the remaining VTK objects and call Delete on them.