Reuse request on Wine side during VST2 processing

The object was constantly being recreated, resulting in memory
allocations caused by creating and destroying the audio buffer vectors.
This commit is contained in:
Robbert van der Helm
2021-05-23 14:57:21 +02:00
parent 6f321649c4
commit 4e81c1c2b3
4 changed files with 23 additions and 18 deletions
+2
View File
@@ -30,6 +30,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
- Redesigned the VST3 audio socket handling to be able to reuse the process data
objects on both sides. This greatly reduces the overhead of our VST3 bridging
by getting rid of all memory allocations during audio processing.
- VST2 audio processing also received the same optimizations. In a few places
yabridge would still reallocate heap data during audio processing.
- Considerably optimized both VST2 and VST3 audio processing by preventing
unnecessary memory operations. As it turns out, the underlying binary
serialization library used by yabridge would always reinitialize the type-safe
+12 -9
View File
@@ -443,8 +443,10 @@ class SocketHandler {
* Read a serialized object from the socket sent using `send()`. This will
* block until the object is available.
*
* @param buffer The buffer to read into. This is used to prevent excess
* allocations when sending audio.
* @param object The object to serialize into. There are also overrides that
* create a new default initialized `T`
* @param buffer The buffer to read into. This is useful for sending audio
* and chunk data since that can vary in size by a lot.
*
* @return The deserialized object.
*
@@ -467,13 +469,13 @@ class SocketHandler {
* @see SocketHandler::receive_multi
*/
template <typename T>
inline T receive_single(SerializationBufferBase& buffer) {
return read_object<T>(socket, buffer);
inline T& receive_single(T& object, SerializationBufferBase& buffer) {
return read_object<T>(socket, object, buffer);
}
/**
* `SocketHandler::receive_single()` with a small default buffer for
* convenience.
* `SocketHandler::receive_single()` into a new default initialized object
* with a small default buffer for convenience.
*
* @overload
*/
@@ -500,14 +502,15 @@ class SocketHandler {
* @see read_object
* @see SocketHandler::receive_single
*/
template <typename T, std::invocable<T, SerializationBufferBase&> F>
template <typename T, std::invocable<T&, SerializationBufferBase&> F>
void receive_multi(F&& callback) {
SerializationBuffer<64> buffer{};
T object;
while (true) {
try {
auto object = receive_single<T>(buffer);
receive_single<T>(object, buffer);
callback(std::move(object), buffer);
callback(object, buffer);
} catch (const boost::system::system_error&) {
// This happens when the sockets got closed because the plugin
// is being shut down
+7 -7
View File
@@ -601,17 +601,17 @@ void Vst2PluginBridge::do_process(T** inputs, T** outputs, int sample_frames) {
input_buffers[channel].begin());
}
const AudioBuffers request{.buffers = input_buffers,
.sample_frames = sample_frames,
.current_time_info = current_time_info,
.current_process_level = current_process_level,
.new_realtime_priority = new_realtime_priority};
AudioBuffers request{.buffers = input_buffers,
.sample_frames = sample_frames,
.current_time_info = current_time_info,
.current_process_level = current_process_level,
.new_realtime_priority = new_realtime_priority};
sockets.host_vst_process_replacing.send(request, process_buffer);
// Write the results back to the `outputs` arrays
const auto response =
const auto& response =
sockets.host_vst_process_replacing.receive_single<AudioBuffers>(
process_buffer);
request, process_buffer);
const auto& response_buffers =
std::get<std::vector<std::vector<T>>>(response.buffers);
+2 -2
View File
@@ -179,7 +179,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
parameters_handler = Win32Thread([&]() {
sockets.host_vst_parameters.receive_multi<Parameter>(
[&](Parameter request, SerializationBufferBase& buffer) {
[&](Parameter& request, SerializationBufferBase& buffer) {
// Both `getParameter` and `setParameter` functions are passed
// through on this socket since they have a lot of overlap. The
// presence of the `value` field tells us which one we're
@@ -216,7 +216,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
plugin->numOutputs);
sockets.host_vst_process_replacing.receive_multi<AudioBuffers>(
[&](AudioBuffers request, SerializationBufferBase& buffer) {
[&](AudioBuffers& request, SerializationBufferBase& buffer) {
// Since the value cannot change during this processing cycle,
// we'll send the current transport information as part of the
// request so we prefetch it to avoid unnecessary callbacks from