Thank you for your recent book and this great framework.
I'm trying to remedy a memory leak issue with the DemoCustomViews_VST example project.
I've built the VST3 from the example, using VS 2017 on Windows 7. ASPIK version 1.6.3.
Using either Reaper or Ableton Live the leak can be encountered by adding a couple of instances of the VST to a track + a couple of instances to the master track. Once the GUI's are closed, then RAM usage quickly begins to accumulate.
It seems that x instances of DemoCustomViews_VST can be placed on x parallel tracks without any problem but chaining them serially creates this issue.
Any advice on this dealing with this?
Hmmmm. Let me check that. The demo custom views do use the lock-free ring buffer but that should manage its own memory. The Custom Views use interfaces for thread-safe communication and the plugin shell is responsible for maintaining those pointers. And, VST uses a class library approach so that could also be an issue.
I will check and get back to you. Glad you are enjoying the book. I put a lot of work (years) into ASPiK so I hope it can be helpful for those looking to get into plugin programming, and/or release product without licensing fees!
OK, I have gotten this sorted out and is now fixed in the new ASPiK SDK 1.6.4
This was actually a lot of fun to track down and correct. First, there were actually two *real* memory leaks (caused by dynamic allocation with new and no corresponding delete) but these were tiny. One was 32 bytes per GUI object, and the other was 380 bytes per GUI instance. Neither of those would be enough for you to see on either the Visual Studio Memory monitor (which is what I used) or the Task manager unless you opened and closed the GUI thousands of times.
But, what you were seeing was "memory creep" in the plugin-side lock-free ring-buffer. There are two of these in the DemoCustomViews project, one on the plugin side and the other on the GUI side. The GUI side of things was fine, and the ring-buffer was being emptied and disposed of correctly when the GUI was closed.
However, if you look the plugin-side code, I am calling the enqueue( ) function to push the audio samples into the buffer. This grows the buffer as needed, and what you were watching was this buffer growing *when there was no GUI present* to eat the samples from the ring-buffer. This caused the memory to appear to be ever-growing after the GUI had closed, or before it had been opened at all, which is how I caught it. And, with the Visual Studio memory monitor in debug mode, you only needed one instance of the GUI to see it happening.
With multiple instances of the same plugin on the same track, you probably also noticed that it would become sluggish when you switched GUIs for the identical plugins. This was the plugin-side ring buffer emptying, after growing larger. With many instances, it would even halt Reaper's visuals, though audio still rendered.
Ultimately, when the process was stopped, the now bloated ring-buffer was destroyed properly, so in that technical sense, there wasn't a memory leak. But, for sure, the longer the plugin ran with no GUI, the more memory-creep happened and it would eventually halt the process at some point.
There were two solutions to this: the first is easy - call the try_enqueue( ) function on the plugin side ring buffer, which does *not* grow the queue in length. This worked fine with the DemoCustomViews plugin that plots the max audio sample that occurred during the buffer fill operation as a simple histogram.
However, for the DemoFFTViews, this caused the FFTs to have long gaps between them, which meant that the GUI only got redrawn with new data about every 750mSec - plenty of slowness to see visually.
If you think about it, what needs to happen is this: when there is a GUI present, the plugin-side ring buffer will need to grow itself out to a point where the GUI can consume the data in FFT sized blocks each time it needs to be updated in a manner that makes smoothly changing graphics. Once that equilibrium point happens, which will be dependent on memory, CPU, system loading, etc..., then the plugin-side buffer will stop growing and everything is OK.
In order to do this, I added an atomic boolean flag variable that is toggled in the processMessage( ) function for the GUI_WILL_OPEN and GUI_DID_CLOSE messages and then checked in processAudioFrame( ). That variable and accompanying functions for the atomic read/write are in the plugincore.h file. Now, when I run the plugin (or any number of them) with the GUI open, the plugin-side ring buffer will grow out to a certain point, then stop as it hits the equilibrium point with the GUI's consumption and the FFTs are smooth and fast-moving as a result. In addition, this removes any sluggishness when switching or closing/opening GUIs. Lastly, when you close the GUI, the plugin-side ring-buffer does not shrink - it stays the same size, but is not used. When you open the GUI back up, the queue begins moving again, with the ring buffer sized correctly already.
I decided to add the same paradigm to the DemoCustomViews project, even though its issue could be solved more simply with the try_enqueue( ) call and now both are functioning correctly.
Thanks for reporting this - it cleaned up a couple of minor but real memory leaks, and also shed light onto that plugin-side buffering issue, plus was fun to debug!
Heroic effort. Thanks heaps for this fix and especially for the detailed and enlightening response.
I had been attempting to disable the customViewDataQueue.enqueue() with a conditional statement in the audio thread as that did address the issue. But I then struggled to send a boolean into the audio thread from the GUI_DID_CLOSE call. So it will be very helpful to check out how you achieved that.
I'm really glad you enjoyed the debug process.
I'll get onto 1.6.4.
Thankyou again for all your work
Most Users Ever Online: 152
Currently Browsing this Page:
Guest Posters: 1
Newest Members:oneday, Phelan Kane, audiocoder, agel, Makai, Abyz, Nonlinear, IgorVish, Arjuna, TomMilne
Moderators: W Pirkle: 470
Administrators: Tom: 74, JD Young: 80, Will Pirkle: 0, W Pirkle: 470