diff --git a/CHANGELOG.md b/CHANGELOG.md index 88d92038..faaa52e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ Versioning](https://semver.org/spec/v2.0.0.html). and preventing memory allocations in the process. - Further optimized VST3 audio processing by preallocating small vectors for 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 counter any potential GUI related timing issues with VST3 plugins. - The deserialization part of yabridge's communication is now slightly faster by diff --git a/src/common/serialization/vst2.h b/src/common/serialization/vst2.h index 0b858ece..6afe9b7d 100644 --- a/src/common/serialization/vst2.h +++ b/src/common/serialization/vst2.h @@ -19,11 +19,13 @@ #include #include -#include "../bitsery/ext/in-place-variant.h" #include #include #include +#include +#include "../bitsery/ext/in-place-variant.h" +#include "../bitsery/traits/small-vector.h" #include "../utils.h" #include "../vst24.h" #include "common.h" @@ -162,6 +164,9 @@ struct ChunkData { * 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 * 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 { public: @@ -177,9 +182,11 @@ class alignas(16) DynamicVstEvents { 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 events; + boost::container::small_vector events; template void serialize(S& s) { @@ -194,14 +201,19 @@ class alignas(16) DynamicVstEvents { * `as_c_events()` method. * * 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` * pointer array, but the actual length of this array is * `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 * heap by hand. */ - std::vector vst_events_buffer; + boost::container::small_vector< + uint8_t, + sizeof(VstEvents) + + ((64 - 1) * + sizeof(VstEvent*))> // NOLINT(bugprone-sizeof-expression) + vst_events_buffer; }; /** diff --git a/src/plugin/bridges/vst2.h b/src/plugin/bridges/vst2.h index d46b2d1d..441d2595 100644 --- a/src/plugin/bridges/vst2.h +++ b/src/plugin/bridges/vst2.h @@ -185,10 +185,10 @@ class Vst2PluginBridge : PluginBridge> { * 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 * 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()`. */ - std::vector incoming_midi_events; + boost::container::small_vector incoming_midi_events; /** * Mutex for locking the above event queue, since recieving and processing * now happens in two different threads. diff --git a/src/wine-host/bridges/vst2.h b/src/wine-host/bridges/vst2.h index bb5ab6ed..5217f017 100644 --- a/src/wine-host/bridges/vst2.h +++ b/src/wine-host/bridges/vst2.h @@ -189,8 +189,13 @@ class Vst2Bridge : public HostBridge { * events they receive but some plugins such as Kontakt only store pointers * to these events, which means that the actual `VstEvent` objects must live * 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 next_audio_buffer_midi_events; + boost::container::small_vector + next_audio_buffer_midi_events; /** * Whether `next_audio_buffer_midi_events` should be cleared before * inserting new events.