Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Preliminary testing with CheckBox seems to indicate a potential 6x improvement in performance for this case (where you have a hundred or so check boxes on the scene). The numbers for TableView were only marginally better – perhaps due to the overhead in CSS / Layout related to the table, although this analysis is speculative.

Reducing Redundant Relayout

I believe we presently do much more work per scene than is required when a single component nested deep in the structure of the scene graph has changed its preferred size and requires layouts to execute. The way this is supposed to work at present is that, when a Node calls requestLayout, it is assumed that this node may have changed in such a way that its preferred size, min size, or max size has changed such that these changes would impact how the node is laid out in its container.

If the container is a Group, then during the layout pass when the child is resized to its new preferred size, this change in size might also impact the size of the Group. If the Group is a child of a layout container, the change in the group size will also impact the layout of items within that parent layout container, and require another layout pass.

In the normal course of affairs, when a node's requestLayout is called, it walks up the tree marking each parent in the tree as also needing to have layout applied. This is because the change in the pref width of a button may in fact impact the pref width of its parent container which may affect the pref width of the parent container's parent container, and so on. If one of those parent containers is a layout root (such as the content pane of a ScrollPane) then we don't walk any further up in the hierarchy since we know a change to the nodes within the layout root will have no impact on the pref width / height of the layout root.

WIth this basic understanding, a few ideas come to mind:

  1. Verify that all of the Nodes (such as Controls) which can be content roots are properly identified as such. For example, the TableView, ListView, and TreeView should be content roots.
  2. When running a layout pass, have the ability to determine whether a dirty layout node's pref / min / max size has changed. If not, then we have no need to run layout on this node, but can proceed to asking the dirty layout children to lay out themselves.
  3. During a typical layout pass, the parent asks each child for its prefWidth, prefHeight, minWidth, minHeight, maxWidth, and maxHeight (or maybe 4 of those 6). It then proceeds to perform the layout algorithm 3 or more times. Suppose I have root R and layout container L with children C1-C3. When R attempts to lay itself out, it first asks L for its prefWidth (say). To figure out its pref width, L must get the pref width / height of C1 - C3 and perform the layout algorithm, so that it knows what its preferred width is. R then asks L for its minWidth (say), and L must then ask for the min width of C1 - C3 and run the layout algorithm to figure out what its min width is. And so forth. Multiple passes on complex layout algorithms is likely hurting us substantially. In retrospect I might have said that min/max was never computed only ever specified manually, which would have probably had a big positive impact in terms of performance. Nevertheless, the fact that we have to run this multiple times even when not strictly necessary is probably a cause of poor performance.