#1596 Scrolling Optimization

yliu Thu 28 Jul 2011

I have a spreadsheet type of application where the spreadsheet hierarchy is as follows:

*->
  TabPane->
      Tab->
           InsetPane->
                      ScrollPane->
                                  GridPane->
                                            BorderPane/Text combinations

I build the rows on demand, that means I do not fill the entire sheet with Cells. When the spreadsheet size horizontally exceeds the size of the TabPane in the window and there are more then 3 rows of entries, scrolling left and right becomes super choppy. I did a stress test with the Task Manager out and was exceeding 30% cpu by scrolling left and right furiously.

However, if I tinker with the scrollpane.fan example like this:

grid := GridPane()
grid.numCols = 30 //<--changed here
200.times |i| { grid.add(Text { text = "WERFAERGAEGERFEREG" }) } //<--and here

I do not run into the same choppy issues, and I'm not hitting the cpu as hard.

I suspect it has something to do with me placing the spreadsheet in the TabPane, but it's necessary for the type of app I am building.

Any suggestions on how to optimize this?

andy Thu 28 Jul 2011

Nothing specific I am aware of - buts its been awhile since I messed with SWT. But loading up a GridPane with lots of widgets can easily cause performance issues - because prefSize and relayout is invoked on every child widget in the tree.

Table-like widgets are almost always better done with a custom Pane - where you can skip at least the prefSize cycle - and usually cut down on unnecessary widgets - since you can add padding directly to child.bounds. Using a Canvas or even dropping down to raw SWT are options as well when you need more performance.

Not sure why you don't seem the same behavior in the example script - could be that BorderPane you are using?

yliu Thu 28 Jul 2011

The options you have given me sound like the direction I need to go to squeeze out as much performance as I can.

I took out the BorderPane's and the choppiness stopped for 3 entries, but around 8 more row entries (104 more widgets) the choppiness came back.

My concern is if I toss the GridPane and write my own custom Table Pane, do you think the ScrollPane will still be a bottleneck? Or should I try to embed my own scrolling system inside the custom pane with some type of caching? I'm unfamiliar with how ScrollPane actually works.

andy Thu 28 Jul 2011

do you think the ScrollPane will still be a bottleneck

Not unless there is a bug in ScrollPane - scrolling should not be invoking relayout - which is the prime suspect for the issues you are seeing.

Its probably worthwhile to dig into the ScrollPanePeer.java code though, and see if something fishy isn't going on that we need to fix.

Anyone else used SWT and seen issues with ScrollPane?

cgrinds Thu 28 Jul 2011

I did a stress test with the Task Manager out and was exceeding 30% cpu by scrolling left and right furiously.

Since you can see it hitting the CPU have you tried profiling? That would be the best way to tell what's going on.

You can grab Yourkit for free at the moment since they're doing their Early Access Program.

yliu Thu 28 Jul 2011

Wow Yourkit is pretty cool, thanks for that.

I profiled the cpu and the first time around I traced the hotspot back to

->fan.fwt.Fwt.eventLoop(Shell)
->fan.fwt.GridPane$onLayout$0.doCall(Widget, long)
  ->org.eclipse.swt.graphics.GC.textExtent(String)
  ->org.eclipse.swt.widgets.Control.setLocation(int, int)
  ->org.eclipse.swt.widgets.Display.sleep()
  ->org.eclipse.swt.widgets.Text.computeSize(int, int, boolean)

the Text.computeSize made sense, because in creating each Text field i set the prefCols, I took that out, and ran another profile. @ 3 entries it was pretty smooth, then I went ahead and tried a 20 entries (315 widgets)..

->fan.fwt.GridPane$onLayout$0.doCall(Widget, long)
  ->org.eclipse.swt.graphics.GC.textExtent(String)
  ->org.eclipse.swt.widgets.Control.setLocation(int, int)
  ->org.eclipse.swt.widgets.Display.sleep()
  ->org.eclipse.swt.widgets.Text.computeSize(int, int, boolean)

this hotspot disappeared however

->fan.fwt.Fwt.eventLoop(Shell)
  ->org.eclipse.swt.widgets.Display.readAndDispatch()
  ->org.eclipse.swt.widgets.Display.sleep()

this hotspot remains, any ideas?

yliu Thu 28 Jul 2011

I replicated the choppiness in the ScrollPane Demo,

class ScrollPaneDemo
{
static Void main()
{
  // build grid of buttons
  grid := GridPane()
  grid.numCols = 30
  200.times |i| { grid.add(Text { text = "WERFAERGAEGERFEREG" }) }

  // build scroll pane
  scrollPane := ScrollPane
  {
    content = grid
    //border = false
    hbar.onModify.add |evt| { echo("hbar = $evt.data") }
    vbar.onModify.add |evt| { echo("vbar = $evt.data") }
  }

  // open in window
  Window
  {
    content = TabPane {
    Tab{text="hi"; InsetPane{content=scrollPane},},
    }
    size = Size(400, 400)
  }.open
}

}

It looks like the Tab/TabPane are acting as some kind of bottleneck, but I'm not exactly sure.

brian Thu 28 Jul 2011

Hi @yliu,

I tried your code you just posted and even cranked it up to 2000 items, still looks really smooth on my desktop machine. But obviously stuff like that can vary greatly on platform to platform.

I think if you want to display 1000s of widgets, that you will likely want to optimize code to avoid repeated "heavy" options like font metrics calculations. You could do this by sticking any "complicated" layout widget like Text or Label in something like a pane that fixes the size and avoids huge number of calls to prefSize.

Login or Signup to reply.