Monday Jul 06, 2009

Adding nodes to a scenegraph

A few of us were recently involved in porting an application written by a third party to the latest JavaFX 1.2. The application was originally written using JavaFX 1.0. The application as such was slow and some parts of the application was too slow to be usable. After porting it to 1.2, the application performance improved significantly compared to its 1.0 version, but it wasn't fast enough. Though we didn't have time to look into the general slowness, we had to look at one particular performance issue. It was kind of recreating a portion of what the app shows depending on user input. This was too slow and the performance was degrading over time. 

It wasn't tough to solve. The code originally was doing the following:

  • Delete a set of nodes present in a group
  • Depending on the input, create another set of nodes
  • Add back the new set of nodes to the group

Creating new nodes is an expensive task. Old nodes had to be gc'd too. Added to that, here we had to delete nodes from a sequence and add back another set of nodes to the sequence. The solution was to rewrite the code to get rid of the expensive node creation and sequence operations. We manipulate the existing nodes by altering its attribute values instead of creating a new set of nodes with new values.

Here is how the actual code looked like:


        delete from content;
        //some call to a java library to get a set of records 
        //depending on user input
        var records: Iterator = getRecords(value).iterator();
        for (records.hasNext()) {
            var record = records.next();
            insert Text {
                content: record.getText()
                translateX: record.getX()
                translateY: record.getY()
            } into content;
        }
  

This was changed to something like the following:


        var records: Iterator = getRecords(value).iterator();
        var index: Integer = 0;
        for (records.hasNext()) {
            var record = records.next();

            //Check if the sequence contains the required number of nodes
            if (index < sizeof content) {
                //modify existing nodes
                var text = content[index];
                text.content = record.getText();
                text.translateX = record.getX();
                text.translateY = record.getY();
                text.visible = true;
            } else {
                //create new nodes only if required
                insert Text {
                    content: record.getText()
                    translateX: record.getX()
                    translateY: record.getY()
                } into content;
            }
            index++;
        }
        //Hide any existing unwanted nodes - don't remove them
        if (index < sizeof content) {
            for (i in [index..sizeof content - 1]) {
                content[index].visible = false;
            }
        }

The above is a simplification of the actual code. The actual application was trying to remove and add back around 100 nodes - it consisted of Text and Line nodes. The modification of code resulted in a significant (2x to 3x) improvement in performance. The gradual degradation of performance was also solved. We still have to look into why the actual code resulted in the repainting becoming progressively slower. This looked like a memory leak somewhere - we have to investigate this.

So, to those who program using JavaFX, keep in mind that you might be sacrificing performance if you are trying to change the scenegraph by removing existing nodes and replacing them with new nodes.

Comments:

This is very important information. Even I have faced this problem when working with 100s of ImageViews. Always re-use nodes.

Posted by Raghu Nair on July 10, 2009 at 10:41 AM IST #

I love this , this is just so cool

Posted by mkv converter on July 14, 2009 at 08:07 PM IST #

Post a Comment:
  • HTML Syntax: NOT allowed