Developing a fast and responsive GUI - is JUCE too bloated?

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

camsr wrote: Fri Jan 01, 2021 10:54 pmNow it looks like Apple wats to be different again. However, Vulkan seems to be the next iteration of OpenGL, and AFAIK if the hardware supports Vulkan, the OS does also.
Apple doesn't support Vulkan. There is a library from Khronos called MoltenVK that translates most of Vulkan to Metal, but last I checked it did have a few limitations, because Metal in general is a bit higher level than Vulkan.

Also Vulkan isn't really the "next iteration of OpenGL" but rather a separate, much lower-level API. While OpenGL is (relatively) easy to use and works great for reasonably simple stuff, the high-level API is not necessarily a great representation of how the hardware actually works, so the driver is forced to do a bunch of stuff to translate and this does introduce a bunch of overhead.

The idea with Vulkan and other low-level APIs is expose much more details on how the hardware actually works and move the responsibility of figuring it out from the driver to the application. This is great if you're trying to get as much performance as possible, but it also means that you need a LOT more code even to draw a simple triangle.

I don't know what the official position is now, but when Vulkan was announced, they actually made it rather clear that it's not a replacement for OpenGL, but rather an alternative. If you want a simple API where the driver will take care of most of the details, use OpenGL. If you want to extract the most out of your hardware and don't mind doing some extra work, then use Vulkan.

Post

camsr wrote: Fri Jan 01, 2021 11:00 pm
mystran wrote: Fri Jan 01, 2021 10:27 pm On the other hand, if you want to use a GPU for rendering, then the type of hardware that doesn't support OpenGL generally is not up to the task anyway.
Then forget even 60hz at that point also? Not quite sure what your point is here, unless you think the plugins should also run on non-x86 platforms without the last decade's graphics.
Your reply doesn't make sense in terms of the part you quoted. All I'm trying to say is that the type of hardware that doesn't support OpenGL is generally so old (and was so slow to begin with) that it is waste of time to try to use it for anything. If you need to use a GPU in order to hit 60Hz then you almost certainly need a "reasonably GPU" and all "reasonable GPUs" come with OpenGL drivers.

Post

Most of what is written in this thread is just not correct. JUCE problem has nothing to do with OpenGL, nothing to do with CGRect mangling and nothing to do with Apple, If you really know the code, you know the problem is JUCE itself.

Here some reasons.
Models, Views and Controllers: mixed everywhere from the ground up.
Just visit the AudioDeviceSelectorComponent's code, then try to break it apart into the mentioned design pattern.. good luck!
Naming: Everything is a "Component" is just the perfect example. A component should be something that you can exchange and will just do the separation well, but often you have to change the "framework" itself to make that even work. Which lead to the fact that you have to compile much much more code then you actually need with native API's. Which in turn leads to increased development time.

Difference of Application and VST/AU/Co targets:
What applies for clever coding of some single Plugin that will run in some host app is fundamental different then coding an App that needs a lot Notarising those days. You end up mimicking native Xcode functionality with Projucer while you have to be extremely careful not to crank up your code base when you have to change it again with Projucer. Projucer introduces a third party parser that is based on the idea that you would create all resources also with Projucer, which has multiple bad implications. This idea is maybe good working for tiny plugins, it is certainly not good working for huge Apps.

Now to the paint problems.
Lets just take the Listbox model..
As component it uses a Viewport when the list doesnt fit in the given rectangle. So for so good, but to make the scrollbar visible the Listbox content is altered inwards instead of doing overlays and masking. Because of that the whole list has to be repainted while scrolling and while figuring out if the scrollbar still needs to be visible. Now the slower you scroll the more your CPU has to work because JUCE just doesnt know if the content needs redraw or not, it takes assumptions from the system which doesnt know it either and it must not know! The solution in JUCE is to just repaint because it's faster with this design pattern then to invest time in the Model to keep the stuff apart. So it kinda works but it comes with massive CPU load for basic functions of mimicking modern UI elements that your users are familiar with how to use them. Well JUCE gives you the freedom to step out it but at the cost of CPU because you cant change your design pattern to draw everything on GPU, impossible. Some stuff always will be hold in BackingLayers so using those wisely should be the answer.

Interlude: Now macOS/iOS specific stuff. Drawing with drawRect is a technique that is based on the idea that you paint once and keep the backing layer until further changes are needed, which is why this method takes a rect as input. It doesnt need to know what is dirty because you set it dirty or other Events can set it dirty as well, which is why everywhere is written that drawRect is not the fastest method. And also the data model that is below your drawing code must be valid at all times. AKA you do it in main thread for good reasons.

Is OpenGL or Metal an answer to that? When you render from within drawRect pattern it is not going well either, you still make use of a design pattern that will have more CPU load then needed and is invalidated from multiple sources. It would be easier to just develop some plain native Metal view and its buffer model then to do it with JUCE and OpenGL. Also because of documentation. To make full use of GPU power you are forced to hold uniform buffers and have a smooth drawing cycle, or even do some charming double or tripple buffer on your flowing data, shuffling data into the shared memory via CPU. As soon you cant have shared memory for content to show you simply shift your idea of GPU load helping you rendering to CPU doing the re-buffering all the time. You technically just increased the complexity of your design pattern the whole task is not faster. The overhead of code to make GPU run smooth eats your intention to just show stuff with simple elements.

The NSViewComponentPeer in juce is as the name says an NS/UI View based model that makes room for the JUCE low level Core Graphics rendering but takes also the invalidation model from NSView from it. On both systems when you want something really fast you put rendering separated in CALayers and avoid drawRect calls at all cost and really draw/updateLayers only when you need to. Or you use the `contents` property of CALayers makes it even easier. When you do that in native coding your CPU is doing almost no work at all, it just flushes masks and backing layers out to the GPU. Nicely working and fast and got very much improved for ages in iOS and OSX. You can make an experiment and just force Juce to render in some CALayer and see the difference in overhead you win because the heavy burden of NSViews is gone.. On iOS this is a little different as UIView is not Cell based view model, it uses CALayers by default making the difference even more obvious.

We could go on and on, in example to the juce::String that is nice but pretty much not in the modern world of multiple languages that make use of `utf32`. Until you find out that std::string supports it pretty well and you almost dont need juce::String and its freaking 80s notation.

as soon your view code contains `std::exp ( std::log(levelXY) )` you know something is wrong.

The really only thing you win with JUCE/c++11/14 is to get rid of the language overhead of Objective-C (string based balanced runtime that has speed limits) and the picky model of Swift and its cumbersome syntax nowadays that leads to code that looks shorter but writing low level with it is still ufff, extra work. When you write some proper App you are forced to develop bindings to native system features because JUCE after all should not be used for anything but took the approach of a framework that tries to solve everything. You could reduce Juce to the bare minimum but then you dont pay (winky winky) the license fee because you end up with almost blank native code of the platforms.

So what is good from JUCE?
the idea of its Timer.
the idea of platform adapters
the idea of different locks and pointer types.
the idea of Messenger and ChangeBroadcaster.
if it's red, reduce gain and use your brain

Post

I agree with ProJucer, but you can now use the new CMake Juce approach.
Otherwise what oszillo says is plain wrong imho.
Olivier Tristan
Developer - UVI Team
http://www.uvi.net

Post

Test approach to really judge:
Do some random mac app with a costume table, fill the table with rendering of all available fonts in your system and show each entry rendered in the font you find and link the entries to render some random example text with the chosen font in a detail view. make it run, now compare..
Open the FontDemo with Projucer, compile and run.
Scroll slowly thru the lists of both examples and look at the CPU load they need to do this yet very same simple job.
Then try to figure out why juce has 40-110% CPU load while the native app has 2% while scrolling.
If you made that test we start talking.
I am not talking about the audio processing of some audio buffers here..
And even less talking about Metal/OpenGL, because for OpenGL or Metal you dont even need JUCE. It is miss-leading talk to compare framework structure with an API that has nothing to do with JUCE.
Its just about the bloat-ness of the framework.
if it's red, reduce gain and use your brain

Post

What you describe is just the implementation of one widget in a particular setting.
I don't see how this relate to some kind of imaginary bloat.
Olivier Tristan
Developer - UVI Team
http://www.uvi.net

Post

What the heck is a widget, refers to wordpress? kidding.
Going with building blocks and copy paste of code for vst/au development into two basic classes with some high priority running stuff and some nested UI made of 5 rectangles of course will never really expose you to the troubles.
Or try to make a window with accessory ui elements or toolbar in its header with juce. :o
Or do some real Multidocument Window Standalone that doesnt use fake tabs in a single window and cant be split so easy as with native code nowadays. That will be fun. Actually the perfect example of the bloated stuff that stays in the way. Juce should have gone plain rectangle support into CALayer instead of taking over stuff (windowing) that is much better home in native code.
if it's red, reduce gain and use your brain

Post

Drawing performance is a pretty big problem in JUCE, I thought I'd link the relevant threads over at the JUCE forum, in case someones wants to read about the details:

https://forum.juce.com/t/solved-repaint ... nent/33711
https://forum.juce.com/t/slow-frame-rat ... macs/35813
https://forum.juce.com/t/juce-coregraph ... jave/30905

Apart from what others have mentioned, there are problems if you use a non-integer scale factor. Because then your front view overlaps "half-pixels" of your background view, so if the front view gets dirty, a big part of the background has to be redrawn as well. The JUCE dev's reply to this was basically: Don't use global scaling, instead put code that does the scaling into every small view. This is bad in terms of code quality, and also means that views "wiggle around" when you resize the UI (because coordinates are being rounded).

Post

oszillo wrote: Wed Feb 02, 2022 2:38 pm What the heck is a widget, refers to wordpress? kidding.
This the classic term, used way before wordpress existed.
GtkWidget or QWidget. Maybe you weren't born though.
oszillo wrote: Wed Feb 02, 2022 2:38 pm Going with building blocks and copy paste of code for vst/au development into two basic classes with some high priority running stuff and some nested UI made of 5 rectangles of course will never really expose you to the troubles.
https://www.uvi.net/falcon.html

A bit more than five rectangle, but hey what do I know.
oszillo wrote: Wed Feb 02, 2022 2:38 pm Or try to make a window with accessory ui elements or toolbar in its header with juce. :o
Or do some real Multidocument Window Standalone that doesnt use fake tabs in a single window and cant be split so easy as with native code nowadays. That will be fun. Actually the perfect example of the bloated stuff that stays in the way. Juce should have gone plain rectangle support into CALayer instead of taking over stuff (windowing) that is much better home in native code.
You are on KvR, so we are talking about Plugin UI. No Multidocument Window Standalone whatsoever.
Want to do that in a cross platform way, just use Qt.
Just want plain rectangle support, see Skia.

Juce has some performance UI issue wise, but it has nothing to do with being bloated.
Olivier Tristan
Developer - UVI Team
http://www.uvi.net

Post

You nailed it. Because when you do the research, read the issues, read the answers specially in JUCE forum you mostly read excuses. Just like yours.
if it's red, reduce gain and use your brain

Post

Dude, calm down.

Post

Dude = nullptr. Some might not realise that on Apple Platforms all Plugins might become bundled in App and the rules are likely to be tightened in some not so distant future.
if it's red, reduce gain and use your brain

Post Reply

Return to “DSP and Plugin Development”