Also preallocate small buffers for VST2 events

This commit is contained in:
Robbert van der Helm
2021-05-23 00:10:03 +02:00
parent 095ca11535
commit e700678a11
4 changed files with 27 additions and 8 deletions
+2
View File
@@ -38,6 +38,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
and preventing memory allocations in the process. and preventing memory allocations in the process.
- Further optimized VST3 audio processing by preallocating small vectors for - Further optimized VST3 audio processing by preallocating small vectors for
event and parameter change queues. event and parameter change queues.
- VST2 audio processing also received the same small vector optimization to get
rid of any last potential allocations during audio processing.
- Changed the way mutual recursion in VST3 plugins on the plugin side works to - Changed the way mutual recursion in VST3 plugins on the plugin side works to
counter any potential GUI related timing issues with VST3 plugins. counter any potential GUI related timing issues with VST3 plugins.
- The deserialization part of yabridge's communication is now slightly faster by - The deserialization part of yabridge's communication is now slightly faster by
+17 -5
View File
@@ -19,11 +19,13 @@
#include <variant> #include <variant>
#include <bitsery/ext/std_optional.h> #include <bitsery/ext/std_optional.h>
#include "../bitsery/ext/in-place-variant.h"
#include <bitsery/traits/array.h> #include <bitsery/traits/array.h>
#include <bitsery/traits/vector.h> #include <bitsery/traits/vector.h>
#include <vestige/aeffectx.h> #include <vestige/aeffectx.h>
#include <boost/container/small_vector.hpp>
#include "../bitsery/ext/in-place-variant.h"
#include "../bitsery/traits/small-vector.h"
#include "../utils.h" #include "../utils.h"
#include "../vst24.h" #include "../vst24.h"
#include "common.h" #include "common.h"
@@ -162,6 +164,9 @@ struct ChunkData {
* Before serialization the events are read from a C-style array into a vector * Before serialization the events are read from a C-style array into a vector
* using this class's constructor, and after deserializing the original struct * using this class's constructor, and after deserializing the original struct
* can be reconstructed using the `as_c_events()` method. * can be reconstructed using the `as_c_events()` method.
*
* Using preallocated small vectors here gets rid of all event related
* allocations in normal use cases.
*/ */
class alignas(16) DynamicVstEvents { class alignas(16) DynamicVstEvents {
public: public:
@@ -177,9 +182,11 @@ class alignas(16) DynamicVstEvents {
VstEvents& as_c_events(); VstEvents& as_c_events();
/** /**
* MIDI events are sent in batches. * MIDI events are sent just before the audio processing call. Technically a
* host can call `effProcessEvents()` multiple times, but in practice this
* of course doesn't happen.
*/ */
std::vector<VstEvent> events; boost::container::small_vector<VstEvent, 64> events;
template <typename S> template <typename S>
void serialize(S& s) { void serialize(S& s) {
@@ -194,14 +201,19 @@ class alignas(16) DynamicVstEvents {
* `as_c_events()` method. * `as_c_events()` method.
* *
* The reason why this is necessary is because the `VstEvents` struct is * The reason why this is necessary is because the `VstEvents` struct is
* actually a variable size object. In the definition in * actuall variable size object. In the definition in
* `vestige/aeffectx.h` the struct contains a single element `VstEvent` * `vestige/aeffectx.h` the struct contains a single element `VstEvent`
* pointer array, but the actual length of this array is * pointer array, but the actual length of this array is
* `VstEvents::numEvents`. Because there is no real limit on the number of * `VstEvents::numEvents`. Because there is no real limit on the number of
* MIDI events the host can send at once we have to build this object on the * MIDI events the host can send at once we have to build this object on the
* heap by hand. * heap by hand.
*/ */
std::vector<uint8_t> vst_events_buffer; boost::container::small_vector<
uint8_t,
sizeof(VstEvents) +
((64 - 1) *
sizeof(VstEvent*))> // NOLINT(bugprone-sizeof-expression)
vst_events_buffer;
}; };
/** /**
+2 -2
View File
@@ -185,10 +185,10 @@ class Vst2PluginBridge : PluginBridge<Vst2Sockets<std::jthread>> {
* function. If they are sent during any other time or from another thread, * function. If they are sent during any other time or from another thread,
* then the host will just discard them. Because we're receiving our host * then the host will just discard them. Because we're receiving our host
* callbacks on a separate thread, we have to temporarily store any events * callbacks on a separate thread, we have to temporarily store any events
* we receive so we can send them to the host at the end of * we receive so we can send them to host on the audio thread at the end of
* `process_replacing()`. * `process_replacing()`.
*/ */
std::vector<DynamicVstEvents> incoming_midi_events; boost::container::small_vector<DynamicVstEvents, 4> incoming_midi_events;
/** /**
* Mutex for locking the above event queue, since recieving and processing * Mutex for locking the above event queue, since recieving and processing
* now happens in two different threads. * now happens in two different threads.
+6 -1
View File
@@ -189,8 +189,13 @@ class Vst2Bridge : public HostBridge {
* events they receive but some plugins such as Kontakt only store pointers * events they receive but some plugins such as Kontakt only store pointers
* to these events, which means that the actual `VstEvent` objects must live * to these events, which means that the actual `VstEvent` objects must live
* at least until the next audio buffer gets processed. * at least until the next audio buffer gets processed.
*
* Technically a host can send more than one of these at a time, but in
* practice every host will bundle all events in a single
* `effProcessEvents()` call.
*/ */
std::vector<DynamicVstEvents> next_audio_buffer_midi_events; boost::container::small_vector<DynamicVstEvents, 4>
next_audio_buffer_midi_events;
/** /**
* Whether `next_audio_buffer_midi_events` should be cleared before * Whether `next_audio_buffer_midi_events` should be cleared before
* inserting new events. * inserting new events.