FBOs with masks. Which way would be more performant?

Hi all,

I need advice on how to better build my code, in regard of performance. At the Moment I do masked FBOs in an own class like this (pseudoCode):


// called just once --------------------------------------------------------------
ofFbo setMask(path) {
    mask.load(path))
    mask.setImageType(OF_IMAGE_COLOR_ALPHA); // for transparency
    mask.getTexture().setSwizzle(GL_TEXTURE_SWIZZLE_A, GL_RED); // for transparency
}

// called every frame --------------------------------------------------------------
ofFbo getContent() {
   ofFbo tempFbo;
  // ... allocate and so on ...

   tempFbo.begin();
   // draw stuff
   tempFbo.end();

   // actually set mask
    if (maskIsSet) {
        tempFbo.getTexture().setAlphaMask(mask.getTexture());
    } else {
        tempFbo.getTexture().disableAlphaMask();
    }

   return tempFbo;
}

This works and gives me an FBO with transparency depending on the mask. The fbo is temporary, which seemed fine. But I have to set the Alpha mask every frame like this. Would it be better to have an fbo that is not temporary in favor of having to set the maks just once? Something like this:

ofFbo fbo; (in header file)

// called just once --------------------------------------------------------------
ofFbo setMask(path) {
    mask.load(path))
    mask.setImageType(OF_IMAGE_COLOR_ALPHA); // for transparency
    mask.getTexture().setSwizzle(GL_TEXTURE_SWIZZLE_A, GL_RED); // for transparency

   // actually set mask
    if (maskIsSet) {
        fbo.getTexture().setAlphaMask(mask.getTexture());
    } else {
        fbo.getTexture().disableAlphaMask();
    }
}

// called every frame --------------------------------------------------------------
ofFbo getContent() {

   fbo.begin();
    // draw stuff
   fbo.end();

   return fbo;
}

I’d be happy for evaluation or hints.
Thanks and have a great day!
oe

hello! “performance” encompasses many things (ressources like RAM, bandwidth/interrupts, thread sync, raw CPU cycles, etc). for sure, any form of allocation should be avoided within the time-critical loops, so as far as hints go, yes your intuition is good: allocate once and re-use. that being said, it also may not be so relevant either considering caches and other optimizations that might be going under the drivers and OS hoods.

the way to really answer any performance question is by profiling the code in the context of your application (that includes your machine/OS/config/etc), so you measure the concrete results. you can fiddle with logging ofGetElapsedTimef() “before and after” (or maybe your IDE has a “real” profiler) but i strongly recommend you get acquainted with GitHub - armadillu/ofxTimeMeasurements: OpenFrameworks add-on to easily measure execution times on different parts of your code.

if you do so, post back your results!

1 Like

Hi @dasoe , an ofFbo can be reused over and over again. Creating and allocating an ofFbo every cycle can be slow, so you could make tempFbo a member of ofApp, allocate it once in ofApp::setup(), and then use it however you like. Setting the texture mask could happen every cycle if you need to change the mask, but these calls shouldn’t slow things down too much (I don’t think).

Another thing that helps with speed is to pass large objects by reference to a function, which then modifies the object (via the reference to it). When you return an ofFbo from a function, at least 1 copy of that ofFbo is made (I think, and maybe more depending). So, a design pattern that works well is something like:

// in ofApp.h, make tempFbo a class member, and add a declaration for getContent()
    void getContent(ofFbo& fbo);
    ofFbo tempFbo;

// in ofApp::update(), send tempFbo to getContent() where it will be updated
    getContent(tempFbo);

// in ofApp.cpp, define getContent(), which now returns nothing;
// it acts directly on the reference provided by its argument
void ofApp::getContent(ofFbo& fbo){
    fbo.begin();
    // draw stuff
    fbo.end();
}

In this case, the function parameter fbo becomes a reference to tempFbo when getContent() is called with it, and thus tempFbo itself gets modified in the function.

As a side note, a const reference can be passed when you don’t want to modify the object in any way. The const keyword will protect the reference from any modifications (intended or otherwise).

Using a reference is key. If you don’t use a reference, a copy of the object will be made and sent to the function. Avoiding copies is important in increasing performance. Additionally, the copy will be used by the function, and not the actual object (via a reference to it), so any changes made to the copy by the function will be lost when the function returns.

1 Like

Wow, even two helpful(!) answers.
Thanks so much to both of you!

@TimChi: I understand what you describe and it’s very helpful. In my real project, it’s more komplex, though. May I ask one more time, to be sure to get it?

ofApp:

// in ofApp.h:
oeScreenManager screenManager;

//in ofApp.cpp (in draw()):
screenManager.draw();

screenManager:

//in oeScreenManager.h:
    vector <oeScreen*> screens;

//in oeScreenManager.cpp (in draw()):
    for (unsigned int i = 0; i < screens.size(); i++) {
        screens[i]->getContent().draw(0, 0);
     }

screen:

//in oeScreen.h:
ofFbo fbo;
ofFbo getContent();

//in oeScreen.cpp
ofFbo oeScreen::getContent(){
    fbo.begin();
    // draw stuff
    fbo.end();
    return fbo
}

I draw instantly and do not have an fbo in screenManager. But if I understand correctly, what you describe (copying) will take place anyway, as I still return an fbo? So there will be a benefit when I define the fbo (as a vector) in “screenManager” instead of in the screen object? From an OO point of view it seems less elegant, but if it prevents the copies…

Thanks again & have a great day!
oe

Hey sure and glad this has been helpful so far!

Here are some ideas to consider:
ofApp:

// in ofApp.h:
oeScreenManager screenManager;

// ofApp::update()
screenManager.update();

// ofApp::draw()
screenManager.draw();

screenManager:

//in oeScreenManager.h:
vector <oeScreen*> screens;

//in oeScreenManager.cpp:
void oeScreenManager::update(){
    for (unsigned int i = 0; i < screens.size(); i++) {
        screens[i]->getContent();
     }
}

void oeScreenManager::draw(){
    for (unsigned int i = 0; i < screens.size(); i++) {
        screens[i]->draw();
     }
}

oeScreen

//in oeScreen.h:
ofFbo fbo;
void getContent();
void draw();

//in oeScreen.cpp
void oeScreen::getContent(){
    fbo.begin();
    // draw stuff
    fbo.end();
}

void ofScreen::draw(){
    fbo.draw(0.f, 0.f);
}

It may help to call getContent() and render into all of the ofFbos in the ofApp::update() phase of the cycle so that they’re ready to draw in ofApp::draw(). This is because OF has some extra stuff to do to render everything to the screen in ofApp::draw(). Having a separate oeScreen::draw() function will facilitate this. And things will still be drawing instantly because both ofApp::update() and ofApp::draw() are called once per cycle.

You can always return large objects by reference if needed, and avoid the copy:

// in oeScreen.h:
inline ofFbo& getFbo() {return fbo;}

There is a const version of this too, but I’ll probably get it wrong so I’ll defer to someone with better const-correctness.

1 Like