This was removed in 4a92034620 after the
VST2 callback prefetching from yabridge 3.2.0. Now with the recent VST3
optimizations, VST3 plugins always outperform VST2 plugins on my system
even when using plugin groups for the VST2 version (and not having to
use plugin groups is of course another good reason to prefer VST3).
Apparently this can actually make a difference in some cases, and the
C++ Core Guideliens recommend doing this on all default constructors,
destructors, and all functions that can not throw (and thus also don't
allocate).
This is very ugly so hopefully I can think of a neater way, but now the
response object is just a set of pointers, so we can avoid all copies
and moves on the Wine side.
Making these thread local statics makes much more sense for their
purpose. The old approach technically wasn't thread safe (even if it was
never an issue) and this gets rid of a data structure.
We do this by using this new `MessageReference<T>` type to avoid copying
our `YaAudioProcessor::Process` struct and the contained `YaProcessData`
object. This is only part of the work, but this redesign lets us keep
the these objects alive on both the plugin and the host side. On the
plugin side, we'll simply serialize the data from the referred to object
without copying it. On the Wine side, we'll write the data to a
persistent thread local object, and then reassign the
`MessageReference<T>` to point to that object. This lets us serialize
'references', thus avoiding potentially expensive allocations. With
these last few changes alone VST3 plugins are already at the same
performance level as our optimized VST2 plugin groups.
This is kind of equivalent to `std::reference_wrapper`, but with default
initialization support (which is UB, but is required for serialization)
and a forward for `T::Response` as used by your sockets API.
This means that, we're now receiving into an existing `YaProcessData`
object, which should reduce the number of allocations on the Wine side
considerably. Next we should also reuse the `YaProcessDataResponse`
object.
I was wondering why I couldn't find the overload for `UniversalTResult`,
and as it turns out we would always use this variant (which
unsurprisingly was the very first overload added). We should of course
have separate overloads for this.
Bitwig prepends some data when passing an `IBStream*` to the plugin, and
when we do copy it iZotope Rx7 plugins cannot load their state (even if
we also copy over the same seek position). Not copying that preamble
fixes the issue, and it seems like it doesn't break anything.
Hopefully. Checking mapped state apparently wasn't enough to prevent
spurious assertion failures on `is_child_window_or_same()`. Now we'll
just use exceptions instead of assertions so we catch them and ignore
them.
This is probably a better idea than trying to be efficient. If we get
two events in a row, then it can be that the map status has changed
between the two events.
VST3 plugins could freeze in REAPER when the plugin sends
`IComponentHandler::performEdit()` followed by
`IPlugFrame::resizeView()` when REAPER simultaneously tries to call
`*::getState()`. Here `*::getState()` gets called from the GUI thread,
while `IPlugFrame::resizeView()` has to be handled on REAPER's GUI
thread, resulting in a deadlock unless we use the plugin-side mutual
recursion mechanism.
I've seen this cause issues with PSPaudioware InfiniStrip.
The VST3 version of Voxengo TEOTE would deadlock in Ardour when Ardour
calls `IEditController::setState()`, the plugin calls
`IUnitHandler::notifyProgramListChange()` in response, and then when
Ardour calls `IUnitInfo::getProgramName()` while handling that callback.
All of these functions have to be called from the same thread in Voxengo
plugins.
Apparently the destructor for C++17/20 conditional initializers are only
run after the else branch, so this would keep the mutex locked while
executing `do_call()`.