DAW Frontend Development Struggles

Published on:

Table of Contents

Edit:


My opinions have changed somewhat since I last wrote this blog post, and I wish to clarify some things. Please read the follow-up in my latest post Clarifying Some Things.

Preface


I would like to write about where my head's been the past several months. If you've noticed that Meadowlark's development has slowed down, this article explains why.

Essentially I've really underestimated how difficult it would be to develop the frontend/GUI of Meadowlark. Not just with how complicated a DAW's GUI is, but also in finding a GUI library that is actually suitable for the task.

I want to use this blog post to do three things. First I want to highlight why this is such a hard problem. Second I want to share my thoughts on the current state of Rust's GUI library ecosystem. And third I want to share some potential paths I can take for the future of Meadowlark.

I would like to hear any thoughts people may have (especially on the part on potential paths for this project). I am most active in my Discord server if you are interested in discussion.

DAW GUIs are complicated


Saying that Meadowlark's frontend/GUI has unusual needs (both performance needs and just features in general) is an understatement. DAWs might just have one of the most complicated GUIs out of any piece of software out there.

Here are some complications I've come across, divided into three parts.

Performance problems

GUI library problems

Complicated logic

My views on the Rust GUI landscape


This video I watched recently brings up a good point about software development (Albeit the video is bit heavy-handed with its message and I might be taking the original message a bit out of context, but I think the point still stands). In the video, there is this chart:

performance-velocity-adaptability chart

Here are what each each of the ends of the triangle mean:

The idea of this chart is that you can't have all three:

Now this brings me to my current views on Rust GUI libraries (and other modern GUI libraries in general, not just Rust). It is my observation that modern GUI toolkits tend to focus too hard on the velocity/adaptability end of the triangle and not the performance end.

Now I totally get why this is. Every developer wants to have a GUI library that is easy to use and gives results quickly. Every developer wants a GUI that is not a nightmare to maintain. And the clever architecture of Rust GUI libraries definitely delivers on those fronts (and at a level that has probably not been done before).

However, I think Rust GUI libraries seriously mistake and/or neglect what it actually takes to have a GUI that has good performance when scaled up to a large project. Of course large and complicated GUIs may not be a target use-case for some or all of these libraries. My point is more to highlight why they won't quite work for Meadowlark.

Now first I should mention that I am definitely aware of premature optimizations, and I am aware that I'm stressing a lot about performance before actually creating the GUI. However, that's not what I'm worried about. What I'm worried about is the GUI library I end up using not even allowing me to do optimizations in the first place if I needed to (and I will very likely need to).

So to start, I should explain what it actually takes for a GUI to be "high performance".

What actually makes a GUI "high performance"?


Rendering performance

The first aspect that makes a "high performance" GUI is quickly rendering the contents onto the screen. While this has definitely gotten easier in the modern age of GPU-acceleration, I don't believe GPU-acceleration alone is a silver bullet:

So I believe it's still important to do what's called "damage tracking", where only the widgets that have changed get redrawn. This is usually done by clearing a rectangular region around a widget, filling the background back into that cleared region, and then redrawing the widget. Though this does definitely get complicated when the "background" is not a flat color, but is instead a hierarchy of other (possibly partially-transparent) widgets.

I also learned recently that damage tracking is not just an optimization on the application-level, but on the operating system level as well. Every OS has some sort of "damage region" concept built into the OS's compositor system, which allows the OS to more efficiently blit small rectangles onto the final screen output instead of needing to blit the entire application's window onto the final output.

That being said, this OS-level optimization is probably not as necessary in the modern age since GPUs are pretty efficient at blitting large textures together.

Input handling performance

The second aspect is efficiently handling input events. If a GUI library sends every mouse/keyboard/animation event to every widget, then that can get really expensive when there are a bunch of nodes in the widget tree.

Iterating a tree structure is not the most efficient to begin with. But there is a bigger problem called "pointer chasing", which happens when you try to excessively dereference a bunch of pointers at once (in this case we dereference each node/widget pointer in order to call its on_event() method).

A good first step is to only send events to the widgets that actually ask for it (although a lot of widgets want both mouse and keyboard input). Mouse input can be optimized by skipping all child nodes if the cursor isn't contained within the bounds of the parent node (however, you then need a system to handle drag operations since those can happen outside the bounds of the widget being dragged).

Update handling performance

The third aspect is efficiently updating the widget tree. When an input event causes a widget to change, the GUI system needs to not only tell the rendering system that the widget has changed and it should be re-rendered, but it also needs to check if any other widgets have changed as a side effect. For example, if the width of a widget changed, that could cause the position of other widgets next to it to change.

My views on the Rust GUI landscape - Part 2


In my experience, every Rust GUI library fails in one or more of those categories listed in the section above.

But I don't believe this to be due to the developer's lack of caring or lack of skill. I think this is an issue rooted in the very architecture that Rust GUI libraries tend to use.

I have found that Rust GUI library architectures (and a lot of modern GUI architectures in general, not just Rust) tend to fall into three categories.

The web-based toolkits

The first category is web-based GUI solutions such as electron and tauri. I understand why these solutions are so popular, it's because they fall square into the "velocity" corner of the triangle. Do you know HTML/CSS or have employees that do? Congratulations, you can create desktop apps!

However, it's no secret that I'm quite against this industry trend of "let's use web tech for everything!":

The immediate-mode toolkits

The second category is immediate-mode GUI solutions such as egui, imgui, and makepad. This architecture is both very quick and easy to use, while also having a high degree of adaptability due to the fact that "the GUI is a function of the data".

However, this is definitely at the cost of performance. Whenever any part of the data changes, it reconstructs/restyles/relayouts the entire widget tree and redraws the entire screen widget-by-widget. While there can be some clever caching optimizations under the hood, the architecture is still flawed in this regard. This performance is not a problem if the app is small or the app already redraws every frame like a video game. But it is a problem when the GUI is as complicated as a DAW GUI.

The Elm-based toolkits

An architecture that is very popular in the Rust ecosystem is the Elm architecture (or some variation of the architecture). This is because it gets around the problem of shared mutability in Rust, while also having a very high degree of adaptability/maintainability due to its data/event-driven nature. GUI toolkits that use a variant of this architecture include Iced, Vizia, Druid Relm, and even Apple's SwiftUI to some extent.

However, the Elm architecture still has drawbacks in terms of performance. While performance is definitely better than immediate-mode (because they are what is called "retained-mode"), these kind of architectures still do a lot of work in order to detect changes to the state of the application. This is mainly to do with their data-driven nature. Because any part of the widget tree can depend on any part of the input data (this input data also includes things like layout and styling), the GUI library has to somehow check the entire input data and the entire widget tree for changes. Each library has a different method for doing this, with varying levels of performance.

Still, I should mention that the alternative to a data-driven approach is to have the user manually update the widget tree themselves. This is definitely more time consuming and more error-prone, so I understand why the industry is gravitating away from it. And I don't dislike the concept. In theory it has the potential to have "good enough" performance at a large scale (of course the actual real-world performance is a different question).

However, there's a much bigger problem with these Rust GUI libraries in particular, which is that none of them actually do any kind of damage tracking for rendering. They redraw the whole screen widget-by-widget every frame, relying heavily on GPU-acceleration in order to make performance not turn into a slideshow. (Although this situation might change for one library which I'll get into later.)

Potential plans moving forward


So all this brings me to my current situation of figuring out the best path forward for Meadowlark. This is the part where I would like to hear your thoughts if you have any.

I think there are three main questions to answer here: Should I stick with Rust for Meadowlark's frontend, what GUI library should I use, and what method is best to actually go about developing the frontend?

Should I stick with Rust?


This first question is definitely a tough one. Meadowlark has been rooted in Rust since the beginning (even starting out as the "Rusty DAW project").

However, from my experience I'm just not sure anymore that the Rust GUI ecosystem is quite there yet (or even that it will be "there" in a year or two). GUI is just so complicated that I'm not sure that passion-driven projects alone are enough to push it to the level of mature C++ libraries. And frankly, I find it harder to get motivated to work on Meadowlark when the underlying technology is unproven and experimental.

But on the flip side, maybe my concerns are unwarranted and Rust is still the best way to go? I don't know. Either way, I'll list the potential options there are for both Rust and C++ to get a better idea on answering this question:

To be clear, with whatever path I choose, I still want to use Rust for the backend as much as possible (namely in my dropseed engine).

It is possible to use both C++ and Rust in the same project thanks to cxx. Of course it will make building more cumbersome, but it's a tradeoff to consider.

Options using native Rust libraries


Here are the native-Rust options I think have any potential to fit my use case.

Vizia

I've used Vizia for the latest attempt at Meadowlark's GUI. It still has potential and I may still decide to stick with it, but I do have quite a few concerns with it.

Pros

Cons

Iced

Iced was actually my original gateway for getting into Rust in the first place. However, I have some serious doubts about its performance. It's possible that performance can be improved in future updates to Iced, but it's currently a gamble.

Pros

Cons

Custom in-house solution

For a while I was working on a concept called Firewheel. The main idea was that it's a low-level GUI library where the user manually updates the widget tree, manually lays out the widgets (with a simple but powerful "anchor" system), and manually assigns widgets to layers to most optimally take advantage of render regions.

However, developing an in-house toolkit most definitely falls into the "performance" corner of the triangle chart.

But as a counter-argument, because it's so low-level, maybe it won't actually take that much time to complete? A custom solution would also have the advantage of being in full control of the feature set.

Then again, it could just be too much work. I'd like to hear other people's opinions on this.

Non-Options

Here I'll list other existing native Rust GUI libraries and why I'm not considering them.

Options using Rust bindings


There is only one I think has any potential to fit my use case.

GTK

I've used gtk-rs in a previous attempt at Meadowlark's GUI. They are bindings to the GTK GUI library which is written in C (and is used by a lot of Linux applications). But again I have some concerns with it:

Pros

Cons

Another potential solution I'll throw out there is maybe we just don't have knobs in Meadowlark, only sliders? It's not ideal, but considering that relative mouse movement support is the only real deal breaker I have with GTK, maybe it's an acceptable tradeoff?

Options using C++


Unfortunately using Rust bindings to these are either nonexistent or are practically unusable due to the incompatible philosophies between Rust and C++. So this means I would need to write Meadowlark's frontend in C++ if I go with one of these options.

FLTK

Pros

Cons

JUCE

Pros

Cons

QT

Pros

Cons

Development plan?


The final question I want an answer to is what is the best way to go about actually developing Meadowlark's frontend? By this I mean who actually does the work of developing it?

No matter what GUI library I choose, developing the frontend is going to take a lot of work due to how complex the logic is.

A part of me is starting to feel like maybe I've bitten off more than I can chew. If I were a company I would just hire a frontend developer, but Meadowlark is currently unfunded so I don't have that luxury (well there's a tiny bit of donations coming in, but definitely not enough to hire anyone).

Now this being an open source project, maybe I could leverage volunteers? However, I'm not sure how well volunteer-driven work will pan out considering just how complex the frontend is. I'm not sure any amount of drafting design documents (which also take quite a bit of work to create) would fix that issue. Ideally I would love if there was one or two people who could dedicated a large amount of time to the frontend. However, I haven't found anyone who is able or willing to do this amount of work for free, and I of course don't blame them for that.

And if you are wondering how I am currently financially supporting myself, I am fortunate enough to have supportive parents to fall back on. I'm currently living at their house on a farm.

I am using this opportunity to try and get Meadowlark off the ground before I start worrying about funding.

Another reason I brought up potentially using C++ is that developers (especially those with experience in the audio industry or the desktop GUI industry) are much harder to come by. If I go with a Rust library, not only will volunteers/employees need to learn Rust, but also the experimental GUI library itself (and Rust GUI libraries tend to have some pretty foreign concepts).

But on the flip-side, maybe the frontend is something I can handle by myself? Maybe I'm just doubting my own abilities too much? I'm not sure. In the end I just want to make sure that I'm allocating my time and resources wisely.

Still, if I do this solo, I think creating some design documents is probably a good idea just to help wrap my head around the complexity.

Final thoughts


After writing this, I'm now kind-of leaning towards the idea of using the Rust bindings to GTK and only having sliders in Meadowlark. It's probably possible to do what Tracktion Waveform does for some of its sliders, which is to show a large pop-up slider when dragging a small slider in order to save space while still allowing for a large degree of control.

But of course I would like to hear any thoughts and ideas you may have. I am most active in my Discord server if you are interested in discussion.