From 93f089eca70d2fe56ec321d452e461248d19fc5a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 28 Apr 2021 12:07:14 +0200 Subject: [PATCH] Always set FTZ instead of gating it behind a flag After a quick round of testing it seems like REAPER doesn't always enable this on the audio thread, but Bitwig, Ardour, Carla and Renoise do. So it should be safe to just get rid of the option and to leave this enabled all the time. --- CHANGELOG.md | 8 +++++++- README.md | 8 -------- meson.build | 6 +++--- src/common/configuration.cpp | 6 ------ src/common/configuration.h | 10 ---------- src/plugin/bridges/common.h | 3 --- src/wine-host/bridges/vst2.cpp | 11 +++++------ src/wine-host/bridges/vst3.cpp | 12 +++++------- 8 files changed, 20 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2628e00e..677a3fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Versioning](https://semver.org/spec/v2.0.0.html). ### Added +- We now always force the CPU's flush-to-zero flag to be set when processing + audio. Most plugins will already do this themselves, but plugins like _Kush + Audio REDDI_ and _Expressive E Noisy_ that don't will otherwise suffer from + extreme DSP usage increases when processing almost silent audio. - Added a new [compatibility option](https://github.com/robbert-vdh/yabridge#compatibility-options) to hide the name of the DAW you're using for a plugin. This can be useful with plugins @@ -48,7 +52,6 @@ Versioning](https://semver.org/spec/v2.0.0.html). plugins. We now explicitly reparent the window to back the root window first before deferring the window closing. This should work around the issue, while still keeping editor closing nice and snappy. -- Fixed the Wine version detection when using a custom `WINELOADER`. - _PSPaudioware InifniStrip_ would fail to initialize because the plugin expects the host to always be using Microsoft COM and it won't initialize it by itself. InfiniStrip loads as expected now. @@ -56,6 +59,9 @@ Versioning](https://semver.org/spec/v2.0.0.html). fix, MIDI events are now deallocated later then when they normally would have to be. - Fixed _UVI Plugsound Free_ crashing during initialization. +- Fixed extreme DSP usage increases in _Kush Audio REDDI_ and _Expressive E + Noisy_ caused by denormals. +- Fixed the Wine version detection when using a custom `WINELOADER`. - Fixed the `cache_time_info` `yabridge.toml` option also affecting the results of other host callbacks during audio processing. diff --git a/README.md b/README.md index 420715f9..d5169f91 100644 --- a/README.md +++ b/README.md @@ -296,7 +296,6 @@ plugin._ | `editor_double_embed` | `{true,false}` | Compatibility option for plugins that rely on the absolute screen coordinates of the window they're embedded in. Since the Wine window gets embedded inside of a window provided by your DAW, these coordinates won't match up and the plugin would end up drawing in the wrong location without this option. Currently the only known plugins that require this option are _PSPaudioware_ plugins with expandable GUIs, such as E27. Defaults to `false`. | | `editor_force_dnd` | `{true,false}` | This option forcefully enables drag-and-drop support in _REAPER_. Because REAPER's FX window supports drag-and-drop itself, dragging a file onto a plugin editor will cause the drop to be intercepted by the FX window. This makes it impossible to drag files onto plugins in REAPER under normal circumstances. Setting this option to `true` will strip drag-and-drop support from the FX window, thus allowing files to be dragged onto the plugin again. Defaults to `false`. | | `editor_xembed` | `{true,false}` | Use Wine's XEmbed implementation instead of yabridge's normal window embedding method. Some plugins will have redrawing issues when using XEmbed and editor resizing won't always work properly with it, but it could be useful in certain setups. You may need to use [this Wine patch](https://github.com/psycha0s/airwave/blob/master/fix-xembed-wine-windows.patch) if you're getting blank editor windows. Defaults to `false`. | -| `force_ftz` | `{true,false}` | Enable the CPU's FTZ flag while processing audio, flushing denormals to zero. This can fix issues with plugins that start showing exponentially increasing DSP load when playback is stopped or when they start processing silence. Defaults to `false`. _This option is currently only available on the master branch._ | | `frame_rate` | `` | The rate at which Win32 events are being handled and usually also the refresh rate of a plugin's editor GUI. When using plugin groups all plugins share the same event handling loop, so in those the last loaded plugin will set the refresh rate. Defaults to `60`. | | `hide_daw` | `{true,false}` | Don't report the name of the actual DAW to the plugin. See the [known issues](#runtime-dependencies-and-known-issues) section for a list of situations where this may be useful. This affects both VST2 and VST3 plugins. Defaults to `false`. _This option is currently only available on the master branch._ | | `vst3_no_scaling` | `{true,false}` | Disable HiDPI scaling for VST3 plugins. Wine currently does not have proper fractional HiDPI support, so you might have to enable this option if you're using a HiDPI display. In most cases setting the font DPI in `winecfg`'s graphics tab to 192 will cause plugins to scale correctly at 200% size. Defaults to `false`. | @@ -334,9 +333,6 @@ editor_xembed = true ["Chromaphone 3.so"] hide_daw = true -["Kush Audio/REDDI.so"] -force_ftz = true - ["SWAM Cello 64bit.so"] cache_time_info = true @@ -448,10 +444,6 @@ include: _Bitwig Studio_, text entry will cause the plugin to crash because Chromaphone uses a different text entry method when it detects Bitwig. You can use the `hide_daw` [compatibility option](#compatibility-options) to work around this. -- **Kush Audio REDDI** suffers from exponentially increasing DSP usage when the - plugin starts processing silence, halting playback and potentially crashing - the DAW. You can enable the `force_ftz` [compatibility - option](#compatibility-options) for the plugin to work around this bug. - The VST2 version of **SWAM Cello** has a bug where it asks the host for the current buffer's time and tempo information for every sample it processes instead of doing it only once per buffer, resulting in very bad performance. diff --git a/meson.build b/meson.build index 263e5fe4..93c4e8e8 100644 --- a/meson.build +++ b/meson.build @@ -38,9 +38,9 @@ compiler_options = [ # Disable the use of concepts in Boost.Asio until Boost 1.73 gets released # https://github.com/boostorg/asio/issues/312 '-DBOOST_ASIO_DISABLE_CONCEPTS', - # When the `force_ftz` option is enabled, we use an intrinsic to force - # flush-to-zero. SSE2 is always enabled in x86_64 CPUs, but when we're - # compiling the 32-bit bitbridge we need to manually add this flag. + # We use an intrinsic to force flush-to-zero. SSE2 is always enabled in x86_64 + # CPUs, but when we're compiling the 32-bit bitbridge we need to manually add + # this flag. '-msse2', ] # NOTE: GCC doesn't 8-byte align doubles in structs on x86 for ABI-compatibilty diff --git a/src/common/configuration.cpp b/src/common/configuration.cpp index 01c8a9ae..919b1157 100644 --- a/src/common/configuration.cpp +++ b/src/common/configuration.cpp @@ -108,12 +108,6 @@ Configuration::Configuration(const fs::path& config_path, } else { invalid_options.push_back(key); } - } else if (key == "force_ftz") { - if (const auto parsed_value = value.as_boolean()) { - force_ftz = parsed_value->get(); - } else { - invalid_options.push_back(key); - } } else if (key == "frame_rate") { if (const auto parsed_value = value.as_floating_point()) { frame_rate = parsed_value->get(); diff --git a/src/common/configuration.h b/src/common/configuration.h index 0c0bee81..5d07bc15 100644 --- a/src/common/configuration.h +++ b/src/common/configuration.h @@ -126,15 +126,6 @@ class Configuration { */ bool editor_xembed = false; - /** - * When using this option, we'll enable the flush-to-zero flag during audio - * processing. This can be useful when the host passes denormals to a plugin - * that doesn't handle those well. An example of such a plugin is REDDI by - * Kush Audio. Some hosts, like Bitwig, will already snap denormals to zero - * for us so this may not be necessary with every host. - */ - bool force_ftz = false; - /** * The number of times per second we'll handle the event loop. In most * plugins this also controls the plugin editor GUI's refresh rate. @@ -214,7 +205,6 @@ class Configuration { s.value1b(editor_double_embed); s.value1b(editor_force_dnd); s.value1b(editor_xembed); - s.value1b(force_ftz); s.ext(frame_rate, bitsery::ext::StdOptional(), [](S& s, auto& v) { s.value4b(v); }); s.value1b(hide_daw); diff --git a/src/plugin/bridges/common.h b/src/plugin/bridges/common.h index 15da2336..2c91dc9f 100644 --- a/src/plugin/bridges/common.h +++ b/src/plugin/bridges/common.h @@ -187,9 +187,6 @@ class PluginBridge { if (config.editor_xembed) { other_options.push_back("editor: XEmbed"); } - if (config.force_ftz) { - other_options.push_back("hack: force flush-to-zero"); - } if (config.frame_rate) { std::ostringstream option; option << "frame rate: " << std::setprecision(2) diff --git a/src/wine-host/bridges/vst2.cpp b/src/wine-host/bridges/vst2.cpp index 8752a6f6..8c0c136c 100644 --- a/src/wine-host/bridges/vst2.cpp +++ b/src/wine-host/bridges/vst2.cpp @@ -169,6 +169,11 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, }); process_replacing_handler = Win32Thread([&]() { + // Most plugins will already enable FTZ, but there are a handful of + // plugins that don't that suffer from extreme DSP load increases when + // they start producing denormals + ScopedFlushToZero ftz_guard; + // These are used as scratch buffers to prevent unnecessary allocations. // Since don't know in advance whether the host will call // `processReplacing` or `processDoubleReplacing` we'll just create @@ -180,12 +185,6 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, sockets.host_vst_process_replacing.receive_multi( [&](AudioBuffers request, std::vector& buffer) { - // HACK: When a plugin doesn't handle denormals properly, we can - // force the FTZ flag to be set to work around this - std::optional ftz_guard = - config.force_ftz ? std::make_optional() - : std::nullopt; - // As suggested by Jack Winter, we'll synchronize this thread's // audio processing priority with that of the host's audio // thread every once in a while diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 8baa19f4..646a9170 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -1211,13 +1211,11 @@ size_t Vst3Bridge::register_object_instance( }, [&](YaAudioProcessor::Process& request) -> YaAudioProcessor::Process::Response { - // HACK: When a plugin doesn't handle denormals - // properly, we can force the FTZ flag to be set - // to work around this - std::optional ftz_guard = - config.force_ftz - ? std::make_optional() - : std::nullopt; + // Most plugins will already enable FTZ, but there are a + // handful of plugins that don't that suffer from + // extreme DSP load increases when they start producing + // denormals + ScopedFlushToZero ftz_guard; // As suggested by Jack Winter, we'll synchronize this // thread's audio processing priority with that of the