Measuring a module's impact on just the UI thread?

I’ve noticed what appear to be opportunities to lower the UI load in my modules (in this case, within the drawLayer() code), but I’d like to be more certain that the changes I’m making actually make a measurable difference.

Have people come up with ways they like to measure the UI thread impact of a module?

@heapdump ?

I measure the time in the draw call. Which tells you nothing about what the gpu is doing, but I’ve found it useful.

Well, it is not so easy. Sadly we have no build-in possibility to measure the models UI load like we can do it with the models CPU usage, so you have to do it yourself.

I used to measure always two things:

  • The draw(...) routine
  • The step() routine

If you set it in relation the the time you max. have per frame, you could easily figure out the load in %.

At 60 fps you have: t=1s/60 which means 100% - just compute how much you need of this time. Also take care to smooth that value at least with some average function.

Cheer, Patrick

Yep, that’s pretty much what I do. I think my “average” function is something pretty dumb, like add all the times together, every hundred times output the sum / 100, and reset the sum. It’s not perfect, but in my case it easily showed that most of my modules were “fine”, and one of them was super fat - so I fixed that one.

1 Like

I have stolen a pretty neat RMS function from bogaudio that does the job perfectly :grimacing: :smile:

Although there are many ways to average sequential data, for many years I have been using “exponential smoothing” to approximate a moving average. This is also a low pass filter. It is also similar to “alpha blending” in graphics.

2 Likes

@Squinky Averaging over the last 100 drawLayer() calls was what I ended up doing. It definitely helped verify that the changes I was making made a difference. Although it’s not clear how low is “too low to care”, but I’m declaring it “done enough”. One helpful thing about this technique was that it quickly highlighted the change in drawLayer() time depending on behavior. When I noticed that, OK, doing this it’s about 350 usec, but doing that it’s about 6000 usec. Important to see that!

@heapdump I hadn’t thought about step(). Good point.

Thanks all.

cool. I think @heapdump is right about your quota. You have perhaps 1/60 of a second to draw everything, so… My general rule of thumb is I don’t want any of my modules to take more than 1% of any particular thing… that way a user could use 100 modules…

Anyway, yeah, when the measurement tells you “this thing is 10X too slow…”

So, at 60 FPS, call it 16,667 usec per frame (optimistically; there is surely overhead from Rack itself). 1% of that would be, 167 usec. I’m certainly exceeding that, at least on my machine. Although I’m pretty sure that it’s not actually calling drawLayer() as often as every frame? I’d have to re-enable the logging to be sure, but that seems unlikely?

Given that, in my case, this is for my improvements on the TextField struct (which is used in VCV’s Notes), which I use for BASICally and my forthcoming Notes-like Fermata (for longer notes), I’d really benefit from some sort of visual caching given that, you know, a text window doesn’t typically change every 60th of a second. Not sure how that could be done. The underlying nvgXxxx classes for displaying text are really being misused for displaying text this way.

You are right, the drawLayer() has to be measured too.

To get a feeling what it means to have “normal” UI/GPU activity I once modified Rack itself measuring all modules and show some statistics, but it was done with an experimental version of Rack 1…

Maybe you can take some ideas out of the code:

And btw: I really like you modules :slight_smile: :wink:

1 Like

FramebufferWidget caches drawn state until marked dirty, so maybe a good candidate for widgets that are expensive to draw and don’t change often. However, for a widget that supports editing text, it’s changing often as you edit. This could be finessed by swapping between widgets for editing vs static display.

1 Like

In my case I made a custom control that did not use frame buffer widget, so it was parsing some svg every draw. Which was terrible.

Most of the examples on Github seem to be people avoiding redrawing SVG, completely reasonable. But my situation is a LOT more interactive (as @pachde alluded to), and I don’t see any examples like that.

I’m been plugging away at it, but FramebufferWidget never seems to actually call my child to draw, no matter how “dirty” I tell it it is. So I’m giving up for now to work on other things. I’ve made substantial improvement, and that’ll do fine. Also, my class hierarchy is really showing its “hack at it until it works” origins, so maybe I should clean that up first.

@heapdump Thank you! I hope you like Fermata when it goes live, which I’m hoping will be sometime Monday (Monday’s seem to be the most common push day for the open source plugins).

well, frame buffer widget does work, but you do need to be careful. And there are plenty of plugins that do all their own drawing and don’t use the VCV widgets at all. My Seq++ it a trival example of that - the note grid is of course not a VCV widget. But there are a ton of ppl who do much more ambitious things…

This is a note for anyone wondering about FramebufferWidget and wondering if I ever figured out how to get it to work.

Turns out, FramebufferWidget is not useful if your child widget(s) use drawLayer() to create the image. Your child widget must use draw().

2 Likes

That sounds like a plain old bug, yes? Have you reported it? Even better, have you tried a local fix?

IIRC that is intentional design on VCV’s part (from a discuss w/ Andrew), and not a bug, but email support and see what they say.

Funny you mention that, @Squinky; it was a CL of yours to your score.h file that convinced me that this could be the problem, because you changed the class from using drawLayer() to using draw() when you added FramebufferWidget.

I have emailed support.