Slow performance when multiple ofTrueTypeFont's drawString calls

Hello guys,

So right now I have 5000 instances of a class ‘Word’ that in its draw function contains the method font.drawString from ofTrueTypeFont class. I have seen some people using ofVboMesh. For instance @arturo suggested this way:

ofVboMesh mesh;
mesh = font.getStringMesh("word",x,y);
font.getFontTexture().bind();
mesh.draw();
font.getFontTexture().unbind();

and if you have multiple words, loop like this:

for every word
    mesh.append(word.mesh)
mesh.draw()

However, in my case each word needs to have its own color and Z position in a 3D space. This is how my draw function from the class ‘Word’ looks right now:

void Word::draw(ofTrueTypeFont &font){
    ofSetColor(col);
    ofPushMatrix();
    ofTranslate(pos_x,pos_y,pos_z);
    font.drawString(text, 0, 0);
    ofPopMatrix();
}

My fps is right now between 15-20. How could I improve the performance?

Thanks in advance,

Joan

1 Like

I am not sure if it’s related to another thing, because your code is doing other things, but:
you can try ofxFontStash or ofxFontStash2:

1 Like

translating a mesh is just adding the translation to each vertex so you can do that before appending it to the total mesh. for the colors you can use colored vertices and add the color with which you want to draw each text to each of it’s vertices, somehting like:

for every word
    for every vertex in word.mesh
        vertex = vertex + position
        color = word color
   mesh.append(word.mesh)

mesh.draw()
1 Like

Thanks, this improved a lot my fps, now is around 40 on my computer. However, on a less powerful computer (MacMini i5 4GB) it didn’t change anything at all…

Thanks Arturo! Can you tell me if Im going the right way with this code?

for (Word & word : words){
    ofVboMesh meshWord;
    meshWord = font.getStringMesh(word.text,0,0);
    vector<glm::vec3> verticesWord = meshWord.getVertices();
    for(int i=0; i<meshWord.getVertices().size(); i++){
        meshWord.setVertex(i, verticesWord[i]+ofVec3f(word.x, word.y, word.z));
        meshWord.addColor(word.col);
    }
    meshWords.append(meshWord)
}

font.getFontTexture().bind();
meshWords.draw();
font.getFontTexture().unbind();

yes that looks good, i’d change:

vector<glm::vec3> verticesWord = meshWord.getVertices();

with

vector<glm::vec3> & verticesWord = meshWord.getVertices();

to avoid making a copy of all the vertices and also if you are going to use the same words all the time you could create a cache of words meshes, for example on a map<string,ofMesh>, to not have to recreate them every frame

That looks very promising! I have done a quick test and seems very fast although Im getting everything flipped vertically. Yes definitely the “map” makes sense, however, if I want to constantly change the color of each word independently would make still sense to use map? How about if I want that each word has a different size?

the flip might happen if you are using a camera to draw, you can pass one more parameter to getStringMesh that tells if you want to flip the mesh or not, which for a camera should be false:

meshWord = font.getStringMesh(word.text,0,0, false);

the map cache should happen before adding the colors and translations so you cache the result from ofTruetypeFont and don’t need to calculate it every frame.

if you want different sizes you might want to allocate different fonts and use a total mesh for each of them or use fonts as shapes instead of texture fonts

1 Like

Perfect the flip problem was solved!

So then, do the cache before:

    ofVboMesh meshWord;
    meshWord = font.getStringMesh(word,0,0,false);
    mesh_words.insert(make_pair(word, meshWord));

and then call it like this:

ofVboMesh meshWord = mesh_words.find(word.text)->second;