gBUI addWindow very very VERY slow

Hello, please help.

Ever since we've upgraded to JME2.0 and upgraded BUI, addWindow() on EventRootNode is horrendously slow. When finest logging is turned on, it seems to constantly re-parse style definitions from file. It takes around 15-20 seconds to open single window.

Any ideas, please?

Largely guessing and giving up at the same time, because I don't know what to do:



during call to addWindow, everything that's called is called through MetaClass.invokeMethod()… is it possible that this is slowing things down? I haven't checked groovy source, but this name smells reflection call.



Log section similar to this one gets repeated for every component on the form, if that helps…



Dec 8, 2008 2:59:36 PM com.jmex.bui.BLabel getParent()
FINER: called from MetaClass.invokeMethod
Dec 8, 2008 2:59:36 PM com.jmex.bui.BStyleSheet findProperty(com.funcom..,unknownClass,'text-align',true)
FINER: called from MetaClass.invokeMethod
Dec 8, 2008 2:59:36 PM com.funcom.tcg.client.ui.vendor.VendorItemButton getStyleClass()
FINER: called from MetaClass.invokeMethod
Dec 8, 2008 2:59:36 PM com.jmex.bui.util.TokReader makeFQClass('vendorwind..',unknownClass)
FINER: called from MetaClass.invokeMethod
Dec 8, 2008 2:59:36 PM com.jmex.bui.BStyleSheet getProperty('vendorwind..','text-align')
FINER: called from MetaClass.invokeMethod
Dec 8, 2008 2:59:36 PM java.util.HashMap get('vendorwind..')
FINER: called from MetaClass.invokeMethod
Dec 8, 2008 2:59:36 PM com.jmex.bui.Rule get({character..,'text-align')
FINER: called from MetaClass.invokeMethod

MetaClass.invokeMethod is a "reflection-like" aperature.  It's something all GroovyObjects have.  I would say, to try this out and see if you get the same speed concern, change the .groovy file to .java then make the necessary changes (e.g. semicolons at the end of lines – I think that's the only difference right now.)  If it speeds it up then there are calls to the BStyleSheet class that are happening in a non-idealistic realm.



I'll guess right now, that is the problem.  I'm starting up my local version.  It might also help if I knew if you had made a different style sheet or added any images, things of that nature.  I'll let you know what I find out with the two metrics I run.

Ok, so I ran through many of the tests that are in the test directory (I'm making a note to move those to something more descriptive like examples or something else outside of the trunk… anyway.)  I haven't had one of them come up in more than 5 seconds.  The average time using IntelliJ is 3 seconds.  The average time using the command line is 2 seconds.



Do you have a snippet of code that I can run against my machines, that will recreate this issue for you?  What OS are you running?  I've got Ubuntu GG, Vista (BLECH!!! – sorry had to say it,) and XP Pro (much nicer.)  I can run other OSs if you're not running one of those three.



As well, any info on your JDK will be helpful.



You can send the code directly or post it here, either way.



timo

I'll run some tests, it sounds like one of the latest trunk changes (GBUI) may be to blame.  I'll look at that now



timo

If its taking a bunch of time when working with a file, perhaps the resource locator is to blame? This has been discussed in the past.

I'll throw a look, I need it like yesterday :). I had no clue its in the gbui code, figured I mixed up something, so I know where to look at least…

Hm, at least that's some guideline. I just cannot figure out why is it slow after upgrade…



Anyway, here's the code, just grip yourself there's quite a lot of it… maybe its worth mentioning that every window has its own stylesheet, in separate file…



Edit:

I've wanted to write rough explanation of the code, but as I was glancing through it, I noted that  synchronizeWithInventory() is changed in the way which modifies underlying GUI on each call. That method is called whenever underlying inventory model is changed (item added or removed), and previously it was just modifying state on custom ItemButton.

This change might make it horrendously slow… we're changing it now to check…





public class InventoryWindow extends BWindowTcg {
  // Which was previous selection?
  private static Object SELECTION = null;
  // Marker objects for selection
  private static final Object ITEMS = new Object();

  public static final String DEFAULT_STYLE = "inventorywindow.container";
  public static final String EXTRA_SLOTS_STYLE = "inventorywindow.container.extraslots";

  private static final int WINDOW_WIDTH = 385;
  private static final int WINDOW_HEIGHT = 464;

  private Inventory.ChangeListener myInventoryListener;
  private BContainer slotsContainer;
  private ItemUser itemUser;
  private Inventory inventory;
  private BScrollPane itemScroll;

  public InventoryWindow(Inventory inventory, ItemUser itemUser) {
    super("Inventory window");

    setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    this.inventory = inventory;
    this.itemUser = itemUser;
    myInventoryListener = new InventoryWindowUpdater(this);

    inventory.addChangeListener(myInventoryListener);
    layoutGui();
    synchronizeWithInventory();
  }

  private void layoutGui() {
    clientArea.add(createTabsContainer());
    clientArea.add(createSlotsContainer());
  }

  private BContainer createTabsContainer() {
    BContainer tabsContainer = new BContainer(new HGroupLayout(GroupLayout.LEFT, GroupLayout.CONSTRAIN));
    // Left indent glue
    tabsContainer.add(new Spacer(15, -1), new GroupLayout.Constraints(true));

    // Both buttons final so that we can access them in listeners
    final BToggleButton itemsButton = new BToggleButton("Items");
    final BToggleButton equipmentButton = new BToggleButton("Equipment");
    equipmentButton.setEnabled(false);

    itemsButton.addListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        equipmentButton.setSelected(false);
        synchronizeWithInventory();
      }
    });
    itemsButton.setPreferredSize(80, 38);
    tabsContainer.add(itemsButton, new GroupLayout.Constraints(true));

    equipmentButton.addListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        itemsButton.setSelected(false);
        synchronizeWithInventory();
      }
    });
    equipmentButton.setPreferredSize(120, 38);
    tabsContainer.add(equipmentButton, new GroupLayout.Constraints(true));
    createTrashCan(tabsContainer);

    // Glue
    tabsContainer.add(new Spacer(), new GroupLayout.Constraints(80));

    // Select the type which was selected upon last creation of this object
    if (SELECTION == null || SELECTION.equals(ITEMS))
      itemsButton.setSelected(true);
    else
      equipmentButton.setSelected(true);

    return tabsContainer;
  }

  private void createTrashCan(BContainer tabsContainer) {
    //For testing deleting items
    tabsContainer.add(new Spacer(15, -1), new GroupLayout.Constraints(true));
    final BLabel trascCanButton = new BLabel("");
    trascCanButton.addListener(new BDropListener() {
      protected void drop(BDropEvent BDropEvent) {
        if (BDropEvent.getDragEvent().getDraggedObject() instanceof DragItemContent) {
          DragItemContent itemButtonContent = (DragItemContent) BDropEvent.getDragEvent().getDraggedObject();
          ClientItem cItem = ((ClientItem) itemButtonContent.getItem());
          itemUser.removeItem(inventory.getId(), Inventory.TYPE_INVENTORY, cItem);
        }
      }
    });

    trascCanButton.setPreferredSize(80, 38);
    trascCanButton.setEnabled(false);
    trascCanButton.setIcon(TcgUI.getIconProvider().getIconForItem(new ClientItem("trashcan")));
    tabsContainer.add(trascCanButton, new GroupLayout.Constraints(true));
  }

  private BContainer createSlotsContainer() {
    TableLayout tableLayout = new TableLayout(4, 1, 1);
    tableLayout.setHorizontalAlignment(TableLayout.CENTER);
    tableLayout.setVerticalAlignment(TableLayout.CENTER);
    BContainer container = new BContainer(tableLayout);

    slotsContainer = new BContainer(tableLayout);
    slotsContainer.setStyleClass(DEFAULT_STYLE);

    itemScroll = new BScrollPaneTcg(slotsContainer, false, true);
    itemScroll.setPreferredSize(345,360);
    container.add(itemScroll);

    for (int i = 0; i < ((ArrayInventory)inventory).getSlotCount(); i++) {
      addSlotButton(i);
    }
    return container;

  }

  private void addSlotButton(int i) {
    final ItemButton b = new ItemButton(i);
    addDefaultListeners(i, b);
    b.setPreferredSize(64, 64);

    if(i >= ArrayInventory.DEFAULT_HEIGHT * ArrayInventory.DEFAULT_WIDTH)
      b.setStyleClass(EXTRA_SLOTS_STYLE);

    slotsContainer.add(b);
  }

  private void addDefaultListeners(int i, final ItemButton b) {
    b.addListener(new BDragListener(b, new DragItemContent(b, inventory, i), new BDragListener.IconRequest() {
      public BIcon getIcon() {
        if (b.getItem() != null)
          return TcgUI.getIconProvider().getIconForItem(b.getItem());
        return null;
      }
    }));

    b.addListener(new BDropListener() {
      protected void drop(BDropEvent BDropEvent) {
        if (BDropEvent.getDragEvent().getDraggedObject() instanceof DragSlotContent) {
          DragSlotContent slotContent = (DragSlotContent) BDropEvent.getDragEvent().getDraggedObject();
          ClientItem clientItem = slotContent.getItem();
          itemUser.unequipItem(slotContent.getEquipDoll().getId(), clientItem);
        }
       else if (BDropEvent.getDragEvent().getDraggedObject() instanceof DragItemContent) {
          DragItemContent itemButtonContent = (DragItemContent) BDropEvent.getDragEvent().getDraggedObject();
          itemUser.arrangeItem(itemButtonContent.getSlotId(), b.getId());         
        }
      }
    });

    b.addListener(new ItemMouseListener(b, inventory.getId(), itemUser));

  }

  @Override
  public void dismiss() {
    super.dismiss();
    inventory.removeChangeListener(myInventoryListener);
  }

  private void synchronizeWithInventory() {
    int index = -1;
    for (InventoryItem item : inventory) {
      index++;
      if(index > slotsContainer.getComponentCount() -1){
        addSlotButton(index);
      }
      ItemButton button = (ItemButton) slotsContainer.getComponent(index);
      button.setItem(item);
    }


    for(int i = inventory.getCapacity(); i < slotsContainer.getComponentCount(); i++){
      slotsContainer.remove(i);
    }

    if(slotsContainer.getComponentCount() <= (ArrayInventory.DEFAULT_HEIGHT * ArrayInventory.DEFAULT_WIDTH)){
      itemScroll.getVerticalScrollBar().setVisible(false);
    }
    else{
      itemScroll.getVerticalScrollBar().setVisible(true);
    }
  }

  private static class InventoryWindowUpdater implements Inventory.ChangeListener {
    private InventoryWindow inventoryWindow;

    private InventoryWindowUpdater(InventoryWindow inventoryWindow) {
      this.inventoryWindow = inventoryWindow;
    }

    public void itemAdded(Inventory inventory, int x, int y) {
      inventoryWindow.synchronizeWithInventory();
    }

    public void itemRemoved(Inventory inventory, int x, int y) {
      inventoryWindow.synchronizeWithInventory();
    }
  }

  private static class ItemMouseListener implements MouseListener, EventListener {
    private ItemButton button;
    private int inventoryId;
    private ItemUser itemUser;
    private boolean pressed;
    private boolean armed;
    private boolean dragged;

    public ItemMouseListener(ItemButton button, int inventoryId, ItemUser itemUser) {
      this.button = button;
      this.inventoryId = inventoryId;
      this.itemUser = itemUser;
    }

    private void buttonClicked(MouseEvent event) {
      InventoryItem item = button.getItem();
      if (item != null) {
        if (event.getButton() == MouseEvent.BUTTON1)
          itemUser.useItem(inventoryId, Inventory.TYPE_INVENTORY, button.getId());
        if (event.getButton() == MouseEvent.BUTTON2)
          itemUser.sellItem(inventoryId, Inventory.TYPE_INVENTORY, button.getId());
      }
    }

    public void mousePressed(MouseEvent event) {
      pressed = true;
      armed = true;
    }

    public void mouseReleased(MouseEvent event) {
      if (armed && pressed && !dragged) {
        buttonClicked(event);
        armed = false;
      }
      pressed = false;
      dragged = false;
    }

    public void mouseEntered(MouseEvent event) {
      armed = pressed;
    }

    public void mouseExited(MouseEvent event) {
      armed = false;
    }

    public void eventDispatched(BEvent event) {
      if (event instanceof MouseEvent) {
        MouseEvent e = (MouseEvent) event;
        if (e.getType() == MouseEvent.MOUSE_DRAGGED  && pressed) {
          dragged = true;
        }
      }
    }
  }
}



Stylesheet (s):
Base stylesheet


/********** DEFAULTS FROM BUI **********/

root {
  color: #FFFFFF;
  font: "Dialog" plain 16;
}

root:disabled {
  color: #BBBBBB;
}

decoratedwindow {
  border: 1 solid #FFFFFF;
}

popupmenu {
  background: solid #00000088;
  border: 2 solid #FFFFFF;
  font: "Dialog" bold 16;
}

component {
}

component:disabled {
}

tooltip_window {
  background: solid #FFFFDD;
  color: #000000;
  border: 2 solid #EEEE99;
  padding: 5;
}

tooltip_label {
 // Non-working color : #000000;
}

menuitem {
  padding: 0 2;
}

menuitem:hover {
  color: #000000;
  background: solid #FFFFFF88;
}

combobox {
  background: image "button_up.png" framexy;
  padding: 3 5;
}

textfield {
  background: solid #00000088;
  padding: 5;
}

textarea {
  background: solid #00000088;
  padding: 5;
}

titlebutton {
  padding: 1 1;
  font: "Arial" bold 12;
  text-align: center;
}

titlebutton:hover {
  color: #E38833;
  background: solid #2a2a90ff;
}

titlebutton:disabled {
  color: #999999;
  border: 1 solid #999999;
}

titlebutton:down {
  padding: 4 6 2 4;
}

titlebutton:selected {
  padding: 4 6 2 4;
}

dialogbutton {
  background: solid #000088;
  border: 1 solid #FFFFFF;
  padding: 3 5;
  font: "Arial" bold 12;
  text-align: center;
}

dialogbutton:hover {
  background: solid #2a2a90ff;
}

dialogbutton:disabled {
  color: #999999;
  border: 1 solid #999999;
}

dialogbutton:down {
  padding: 4 6 2 4;
}

dialogbutton:selected {
  padding: 4 6 2 4;
}

statusbar {
  background: solid #880000;
  border: 1 solid #FFFFFF;
  padding: 3 5;
  font: "Arial" bold 12;
}

titlebarbutton {
  text-align: left;
  padding: 0 0 0 0;
}

buttonbar {
  padding: 6 30;
}

titlemessage {
  font: "Arial" bold 12;
  color: #FFFFFF;
  padding: 5 5;
  text-align: left;
}

statusmessage {
  font: "Arial" bold 10;
  color: #FFFFFF;
  padding: 5 5;
  text-align: left;
}

message {
  font: "Arial" bold 12;
  color: #FFFFFF;
  padding: 10 40;
  text-align: left;
}

greymessagebg {
  font: "Arial" bold 12;
  color: #FFFFFF;
  background: solid #999999;
  padding: 10 40;
  text-align: left;
}

labeltext {
  padding: 5 5;
}

/********** TCG SPECIFIC DEFAULTS **********/

window {
  background: blank;
}

transparentwindow {
  background: image "window_transparent.png" framexy;
}

scrollbar_hthumb {
  background: image "skin/hscrollbar.png" framexy;
}

scrollbar_vthumb {
  background: image "skin/vscrollbar.png" framexy;
}

scrollbar_hwell {
  background: image "skin/inner_darkblue_2.png" framexy;
  padding: 12 0;
}

scrollbar_vwell {
  background: image "skin/inner_darkblue_2.png" framexy;
  padding: 0 12;
}

scrollbar_hless {
//  icon: image "scroll_left.png";
  size: 0 0;
}

scrollbar_vless {
//  icon: image "scroll_up.png";
  size: 0 0;
}

scrollbar_hmore {
//  icon: image "scroll_right.png";
  size: 0 0;
}

scrollbar_vmore {
//  icon: image "scroll_down.png";
  size: 0 0;
}

hslider {
  background: image "skin/inner_darkblue_2.png" framexy;
  icon: image "skin/hscrollbar.png";
}

vslider {
  background: image "skin/inner_darkblue_2.png" framexy;
  icon: image "skin/vscrollbar.png";
}

button {
  background: image "skin/inner_orange_4.png" framexy;
  padding: 3 5;
  text-align: center;
}

button:hover {
  color: #FF00FF;
}

button:down {
  background: image "skin/inner_darkblue_4.png" framexy;
  padding: 4 4 2 6;
}

button:selected {
  background: image "skin/inner_darkblue_4.png" framexy;
  padding: 4 4 2 6;
}

button:disabled {
  background: image "skin/inner_lightblue_4.png" framexy;
  text-align: center;
  color: #999999;
}

togglebutton {
  background: image "skin/inner_lightblue_4.png" framexy;
  padding: 3 5;
}

togglebutton:down {
  background: image "skin/inner_orange_4.png" framexy;
  padding: 4 4 2 6;
}

togglebutton:selected {
  background: image "skin/inner_orange_4.png" framexy;
  padding: 4 4 2 6;
}

checkbox {
  icon: image "skin/checkbox_empty.png" centery;
}

checkbox:selected {
  icon: image "skin/checkbox_checked.png" centery;
}

checkbox:disselected {
  icon: image "skin/checkbox_checked.png" centery;
}

label {
  background: solid #00000000; /*background: image "skin/inner_lightblue_4.png" framexy;*/
}

label_rightaligned {
  parent: label;
  text-align: right;
}

label_centeraligned {
  parent: label;
  text-align: center;
}

titlebar {
  background: image "skin/inner_lightblue_4.png" framexy; /*background: solid #687190;*/
}

minimizebutton {
  background: image "skin/inner_orange_4.png" framexy;
  size: 38 38;
}

minimizebutton:hover {
  background: image "skin/inner_orange_4.png" framexy;
  size: 38 38;
}

minimizebutton:down {
  background: image "skin/inner_darkblue_4.png" framexy;
  size: 38 38;
}

maximizebutton {
  background: image "skin/inner_orange_4.png" framexy;
  size: 38 38;
}

maximizebutton:hover {
  background: image "skin/inner_orange_4.png" framexy;
  size: 38 38;
}

maximizebutton:down {
  background: image "skin/inner_darkblue_4.png" framexy;
  size: 38 38;
}

closebutton {
  background: image "skin/inner_orange_4.png" framexy;
  size: 38 38;
}

closebutton:hover {
  background: image "skin/inner_orange_4.png" framexy;
  size: 38 38;
}

closebutton:down {
  background: image "skin/inner_darkblue_4.png" framexy;
  size: 38 38;
}



InventoryWIndow's stylesheet:


/********** INVENTORY WINDOW **********/

inventorywindow.container {
  background: image "skin/inner_darkblue_22.png" framexy;
  /* padding: top, right, bottom, left; */
  padding: 15 15 15 15;
}

inventorywindow.container.extraslots {
  parent: inventorywindow.container;
  background: image "skin/inner_darkblue_highlight_red_15.png" centerxy;
}

Coleague fixed the problem.



When I tried to disable logging via our logging.properties (we also use java.util.logging), it didn't work. I haven't had sources then so I presumed that gbui uses some customized logger, and disabled logging only at handler level - I've set Console sensitivity to INFO.



Coleague found correct logger name, and then disabled that logger.

Then everything gained superspeed boost.

I thought disabling handler would have same effect, but obviously it doesn't.



Sorry for trouble for such stupid problem, guys.

No problem!  If you send me info on what your colleague did, I'll add it to a wiki entry.  This is the kind of thing that will come up in the future, I'm sure.

Sure, np!



If you ended up having this problem, that means that you have custom logging.properties file or some other kind of configuration of java.util.logging, which logs FINEST levels to handlers. Name of gbui logger is

'com.jmex.bui' and for groovy 'methodCalls'. So just configure with these lines:



com.jmex.bui.level=WARNING
# (for Groovy, used by bui)
methodCalls.level=WARNING