From 4e81c1c2b3a0edd1a4c36bf41138e31aeb95d5f2 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 23 May 2021 14:57:21 +0200 Subject: [PATCH] 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. --- CHANGELOG.md | 2 ++ src/common/communication/common.h | 21 ++++++++++++--------- src/plugin/bridges/vst2.cpp | 14 +++++++------- src/wine-host/bridges/vst2.cpp | 4 ++-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e587ed1d..07a44176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/common/communication/common.h b/src/common/communication/common.h index d71cb7ab..475eebee 100644 --- a/src/common/communication/common.h +++ b/src/common/communication/common.h @@ -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 - inline T receive_single(SerializationBufferBase& buffer) { - return read_object(socket, buffer); + inline T& receive_single(T& object, SerializationBufferBase& buffer) { + return read_object(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 F> + template F> void receive_multi(F&& callback) { SerializationBuffer<64> buffer{}; + T object; while (true) { try { - auto object = receive_single(buffer); + receive_single(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 diff --git a/src/plugin/bridges/vst2.cpp b/src/plugin/bridges/vst2.cpp index 9d043858..37966ead 100644 --- a/src/plugin/bridges/vst2.cpp +++ b/src/plugin/bridges/vst2.cpp @@ -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( - process_buffer); + request, process_buffer); const auto& response_buffers = std::get>>(response.buffers); diff --git a/src/wine-host/bridges/vst2.cpp b/src/wine-host/bridges/vst2.cpp index 709f5826..39d793aa 100644 --- a/src/wine-host/bridges/vst2.cpp +++ b/src/wine-host/bridges/vst2.cpp @@ -179,7 +179,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, parameters_handler = Win32Thread([&]() { sockets.host_vst_parameters.receive_multi( - [&](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 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