FramebufferWidget Question

Working example based on response from @Vortico

// we want to draw stuff procedurally only when things change
// suppose we have SomeModule with an integer "someValue" and a bool "dirty" 
// whenever "someValue" changes "dirty" gets set to true

// create a container for widgets that draw things
struct SomeWidgetBuffer : FramebufferWidget{
  SomeModule *module;
  SomeWidgetBuffer(SomeModule *m){
    module = m;
  }
  void step() override{
    if(module->dirty){
      FramebufferWidget::dirty = true;
      module->dirty = false;
    }
    FramebufferWidget::step();
  }
};

// create widget that draws something based on values of the module
struct SomeDrawingWidget : VirtualWidget{
  SomeModule *module;
  SomeDrawingWidget(SomeModule *m){
    module = m;
    box.pos = Vec(0,0);
    box.size = Vec(100, 100);
  }
  void draw(NVGcontext *vg) override{
    printf("drawing stuff only once\n");
    nvgBeginPath(vg);
    nvgRect(vg, 0, 0,box.size.x, box.size.y);
    nvgFillColor(vg, nvgRGB(module->someValue,0,0));
    nvgFill(vg);
  }
};

// then somewhere inside the ModuleWidget's constructor
{
  SomeWidgetBuffer *fb = new SomeWidgetBuffer(module);
  SomeDrawingWidget *dw = new SomeDrawingWidget(module);
  fb->addChild(dw);
  addChild(fb);
}

ORIGINAL POST :

Sorry if obvious, but could anyone explain what’s the proper way to use the FramebufferWidget with procedural drawing instead of SVGs? I’m using nanovg calls inside the widgets draw function to render everything on my modules (feels more at home than fiddling with inkscape) and I’d like to optimize things that don’t change much. Any open source module doing this?

Let’s say I have a widget with some drawing function like this

struct SomeWidget : FramebufferWidget {
  SomeModule *module;
  SomeWidget(SomeModule *m) {
    module = m;
  }
  void drawStuff(NVGcontext *vg){
    nvgBeginPath(vg);
    nvgRect(vg, 0,0,10,10);
    nvgFillColor(vg, nvgRGB(module->someValue,0,0));
    nvgFill(vg);
  }
};

and the module has a bool that gets set to true whenever someValue is changed, then I’d like to update the framebuffer with drawStuff, where should I check for the bool and how to update the framebuffer?

A somewhat related question, is there a way to monitor the drawing performance of modules? Afaik the module consumption measurement in Rack is independent from rendering, but I’ve noticed with rcomian’s port that limiting the fps to 30 or 15 can help a lot with audio dropouts, so the profiler could report 10ms on a module while it is drawing so many things that it is lagging out Rack. :slight_smile:

2 Likes

I’m interested in this as well. At present, I do this:

struct SomeWidget : TransparentWidget, FramebufferWidget {

void draw(NVGcontext* vg) override {
   if (invalidCache) {
        // do the heavy lifting here
       invalidCache = false;
   }
   // draw the contents here as quickly as I can
   FramebufferWidget::draw(vg);
   dirty = false;
};

bool invalidCache = false;

};

I see that widgets all have bool dirty = true and the comment suggests that this affects when children are rendered, but I must be using it wrong because my display widget gets drawn all the time no matter what.

Yes, I tried something similar with the dirty and it didn’t work. What do you refer to as heavy lifting? In my case it is the drawing itself.

Neither of the above methods are correct. FramebufferWidget draws children widgets. Put a widget inside it and it will be cached in a framebuffer. In the docstring:

When dirty is true, its children will be re-rendered on the next call to step().

In v1, you can override FramebufferWidget::drawFramebuffer(), but this is for calling custom OpenGL or for creating your own NanoVG context for some reason.

My module does a substantial amount of layout (it draws a polyphonic sequence as a musical score) so the repeated drawing is limited to the elements that are visible – I’m not trying to make vg itself faster, not yet anyway.

Ah ok, thanks for clarifying!
So one has to create a container FramebufferWidget then addChild other widgets to that.

@cclark2 I see, so you are caching for example positions to render notes to. Cool!
Writing rendering code inside shaders is definitely something I want to explore with Rack. It could be used for further optimalizations in some cases.

I’m still missing something. If there are more docs or an example you can point me to, that would be great. I’ll keep reading the source and look for the answer there as well.

I changed my widget to look like:

struct MyModuleWidget : ModuleWidget {
   MyWidget(MyModule* module) : ModuleWidget(module) {
    ...
    module->frameBuffer = new FramebufferWidget();
    Widget* someWidget = new SomeWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_WIDTH * 2),
                 Vec(RACK_GRID_WIDTH * 12, RACK_GRID_WIDTH * 9), module);
    module->frameBuffer->addChild(someWidget);
    ...
};

and then in some param widget:

     module->frameBuffer->dirty = true;

but so far nothing draws.

I’m guessing that I haven’t told FramebufferWidget how big it is, or where to draw. Duh.

In v1 you don’t need to set the size of the FramebufferWidget, but in v0.6 you might have you. The default position of Widgets is Vec(0, 0).

It’s working! I’ll update the original post…
@cclark2 Under 0.6 I found that the FbWidget doesn’t need box.size to be set, but the children does.

In your example, you’re failing to call the superclass step().

Do I need to? It works for me as it is (under 0.6).

You need to in v1. I don’t remember for v0.6.
When overriding a method in OOP, the contract is to call the superclass somewhere in the method unless you explicitly don’t want the default behavior.

Also, I personally wouldn’t subclass FramebufferWidget at all. I’d “sandwich” a FramebufferWidget between my custom Widget (which handles events) and my custom drawable Widget. You don’t have to do this, but this pattern is more straightfoward/general.

What do you mean by sandwhich?

I got mine working, sort of. The fills and strokes draw, but text does not. Curious.

In v0.6, FramebufferWidget draws using a separate NanoVG context. In v1, it uses the same context.

Looks to me that fonts are hard-coded to only use the gVg context in v0.6. I’ll try hacking my source locally to use support gFramebufferVg as well.

(later)
My hack worked. I added Font::loadFb() and a new Font constructor and now my module is drawing more or less correctly. Unfortunately, this means I won’t be able to make it available for v0.6. Hmmm. I wonder if I can hack my module to hold the change instead of Rack…

(later)
In the end, I was able to revert my Rack hacks and put the code directly in my module.