Help with libRocket performance

Discussion of features and help with problems encountered while using libRocket

Help with libRocket performance

Postby iantillery on Fri Apr 03, 2015 2:36 am

Despite lots of optimization, changing, rendering and compositing the DOM in Chrome still takes between 6-8ms with spikes up to 40ms, which is unacceptable for maintaining 60 FPS. We're evaluating libRocket for use with our WebGL-based game (compiled using Emscripten), and it's a strong contender. However, when lots of elements need to be updated (and not even moved) it's spending too much time in ElementDocument::UpdateLayout and Element::Render and I'm hoping you can help.

In order to simulate the conditions in which libRocket will run during our game, I devised a demo to update lots of fixed-position text every frame. During initialization, 30 Documents are created from demo.rml (I removed the body so only the title element is showing and fixed the title width to 300px in invader.rcss) and 30 ElementText nodes are created with random text. On each frame, each document's title element's text node is removed and replaced with one of the 30 created during initialization:

Code: Select all
#define NUM_WINDOWS 30
static Rocket::Core::Context *Context;
static Rocket::Core::ElementDocument* docs[NUM_WINDOWS];
static Rocket::Core::ElementText* titles[NUM_WINDOWS];

void MainLoop() {
    ...
    int i = rand() % NUM_WINDOWS;
    for (int j = 0; j < NUM_WINDOWS; j++) {
        Rocket::Core::Element* parent = docs[j]->GetElementById("title");
        Rocket::Core::ElementText* child = (Rocket::Core::ElementText*) parent->GetChild(0);
        parent->RemoveChild(child);
        parent->AppendChild(titles[(i + j) % NUM_WINDOWS], true);
    }
    Context->Render();
    ...
    Context->Update();
}

int main() {
    ...
    for (int j = 0; j < NUM_WINDOWS; j++) {
        docs[j] = Context->LoadDocument("demo.rml");
        Rocket::Core::String contents = Rocket::Core::String(50, "test [\u0411\u0416\u042F] %d", rand());
        Rocket::Core::ElementText* title = docs[j]->CreateTextNode(contents);
        titles[j] = title;
        if(docs[j]) {
            docs[j]->Show();
            docs[j]->RemoveReference();
            docs[j]->SetProperty("top", Rocket::Core::String(10, "%dpx", (j%10)*75));
            docs[j]->SetProperty("left", Rocket::Core::String(10, "%dpx", (int)(floor(((float)j)/10.0))*400 ));
        }
    }
    ...
}


It looks like this, with titles updating every frame:

Image

On a 2013 MacBook Pro running OS X 10.10.2 and Chrome 41, the main loop takes about 5.3ms:

  • 0.8ms in Element::AppendChild
  • 2.7ms in Element::Document::UpdateLayout
  • 1.7ms in Element::Render
  • 0.1ms in Element::Update

You can download the CPU profile from here. (Open the Profiles tab under Chrome's Developer Tools and click "Load" to view it, then left-click-drag and use the scroll wheel to navigate frames.) An average frame looks like this:

Image

Speifically, a lot of time is spent in LayoutEngine::FormatElement as well as in positioning the text.

Is there any faster way which I can use to update element text? Can I mark elements as fixed so that their boxes aren't recalculated? Can I cache the rendered/positioned text to save time there? Any help in making this faster would be appreciated.
iantillery
 
Posts: 1
Joined: Fri Apr 03, 2015 2:02 am

Return to Using libRocket


cron