Instead of the parent Window. Tracktion Waveform does some weird things
with its VST2 editor embedding, so with the old approach this would
cause mouse clicks to be offset 27 pixels vertically and one pixel
horizontally.
On the Wine side. Instead of always having it enabled and disabling it
when it could potentially hurt (i.e. when handling GUI related things),
we'll now only enable it when it's potentially beneficial. This way we
don't have to constantly switch scheduling policies on the GUI thread.
While a VST2 plugin is being initialized in a plugin group, all host
callbacks would go over that new plugin instance's sockets. This would
cause a race condition if the host processes audio while loading
plugins. This issue has been there since the introduction of plugin
groups, but it's only noticeable in Bitwig Studio, and only when using
over thirty instances of the same plugin in a plugin group.
PG-8X in REAPER has the same mutual recursion limitation the Voxengo
plugins had in Renoise, but with `effGetProgramName()` instead of
`effGetProgram()`.
This basically changes the default small vectors during VST2 event
processing from 256 bytes to the size of a `DynamicVstEvents`
object (which also includes a small_vector to hold MIDI events without
allocating) and makes them thread local. We already have a similar
optimization for VST3. There it's a bit neater since we already had to
separate audio processing functions from non-time critical functions.
Here we don't have that separation, so we just made these buffers thread
local, large enough to hold our predefined number of events, and we then
just shrink them to fit if these buffers grow even more (which can only
happen after reading or writing chunk data).
The change doesn't specifically target `effProcessEvents()`, but that's
where you would see the differences. This is also relevant for
`audioMasterProcessEvents()`.
I once read years ago somewhere on Stack Overflow that `std::vectors`
with that are preinitialized to a default size would allocate the
initial capacity on the stack. This of course doesn't make any
sense (run time sized stack allocations can cause all kinds of issues),
so we were still allocating with our default 64-byte sized buffers, but
just not as often.
This does what we did for a few functions in the last few commits for
every function. We now use either the `std::invocable` concept or our
own `invocable_returning` concept wherever possible to make sure we pass
function types to these template functions, since constraint errors are
a lot more readable than template deduction errors. And instead of
having to specify the return type as a template argument, we now just
use `std::invoke_result_t<F>` instead. The VST3 message handling
functions are still using the good old `typename F` since those are
overloaded polymorphic functions. This was also a good moment to modify
`AdHocSocketHandler::send()` to allow functions returning void (this got
rid of an old fixme where we had to return some dummy value from a
function instead of just not returning anything).
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.
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.
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.
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()`.
For individually hosted plugins this will behave the same. For plugin
groups it would have been nice if we could shut down individual plugins,
but dangling threads are apparently a real issue. This should be
equivalent in all use cases.
This also reverts commit bda9a0b75f.
We would never try to shut the group host down if nothing ever tried to
connect to it. This could happen when the native host gets killed after
initializing the yabridge plugin but before it gets the chance to
request the group host process to host a plugin.
This will let us more or less gracefully handle failing host callbacks
during initialization. We cannot catch this from anywhere else since
this these functions get called from unmanaged code.