1
0
Fork 0

Make sure FTZ is always enabled

This commit is contained in:
Robbert van der Helm 2022-02-03 16:18:24 +01:00
parent dfedd7b2c4
commit e642fb5ff8
3 changed files with 55 additions and 2 deletions

View file

@ -118,7 +118,6 @@ impl Plugin for Gain {
} }
fn process(&mut self, buffer: &mut Buffer, _context: &dyn ProcessContext) -> ProcessStatus { fn process(&mut self, buffer: &mut Buffer, _context: &dyn ProcessContext) -> ProcessStatus {
// TODO: The wrapper should set FTZ if not yet enabled, mention ths in the process fuctnion
for samples in buffer.iter_mut() { for samples in buffer.iter_mut() {
// Smoothing is optionally built into the parameters themselves // Smoothing is optionally built into the parameters themselves
let gain = self.params.gain.smoothed.next(); let gain = self.params.gain.smoothed.next();

View file

@ -93,7 +93,6 @@ pub trait Plugin: Default + Send + Sync {
/// ///
/// TODO: Provide a way to access auxiliary input channels if the IO configuration is /// TODO: Provide a way to access auxiliary input channels if the IO configuration is
/// assymetric /// assymetric
/// TODO: Handle FTZ stuff on the wrapper side and mention that this has been handled
/// TODO: Pass transport and other context information to the plugin /// TODO: Pass transport and other context information to the plugin
fn process(&mut self, buffer: &mut Buffer, context: &dyn ProcessContext) -> ProcessStatus; fn process(&mut self, buffer: &mut Buffer, context: &dyn ProcessContext) -> ProcessStatus;
} }

View file

@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::cmp; use std::cmp;
use std::marker::PhantomData;
use std::os::raw::c_char; use std::os::raw::c_char;
use vst3_sys::vst::TChar; use vst3_sys::vst::TChar;
use widestring::U16CString; use widestring::U16CString;
@ -92,6 +93,9 @@ pub fn u16strlcpy(dest: &mut [TChar], src: &str) {
/// `assert_no_alloc` if needed, while also making sure that things like FTZ are set up correctly if /// `assert_no_alloc` if needed, while also making sure that things like FTZ are set up correctly if
/// the host has not already done so. /// the host has not already done so.
pub fn process_wrapper<T, F: FnOnce() -> T>(f: F) -> T { pub fn process_wrapper<T, F: FnOnce() -> T>(f: F) -> T {
// Make sure FTZ is always enabled, even if the host doesn't do it for us
let _ftz_guard = ScopedFtz::enable();
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(all(debug_assertions, feature = "assert_process_allocs"))] { if #[cfg(all(debug_assertions, feature = "assert_process_allocs"))] {
assert_no_alloc::assert_no_alloc(f) assert_no_alloc::assert_no_alloc(f)
@ -100,3 +104,54 @@ pub fn process_wrapper<T, F: FnOnce() -> T>(f: F) -> T {
} }
} }
} }
/// Enable the CPU's Flush To Zero flag while this object is in scope. If the flag was not already
/// set, it will be restored to its old value when this gets dropped.
struct ScopedFtz {
/// The old FTZ mode to restore to, if FTZ was not already set.
old_ftz_mode: Option<u32>,
/// We can't directly implement !Send and !Sync, but this will do the same thing. This object
/// affects the current thread's floating point registers, so it may only be dropped on the
/// current thread.
send_sync_marker: PhantomData<*const ()>,
}
impl ScopedFtz {
fn enable() -> Self {
cfg_if::cfg_if! {
if #[cfg(target_feature = "sse")] {
let mode = unsafe { std::arch::x86_64::_MM_GET_FLUSH_ZERO_MODE() };
if mode != std::arch::x86_64::_MM_FLUSH_ZERO_ON {
unsafe { std::arch::x86_64::_MM_SET_FLUSH_ZERO_MODE(std::arch::x86_64::_MM_FLUSH_ZERO_ON) };
Self {
old_ftz_mode: Some(mode),
send_sync_marker: PhantomData,
}
} else {
Self {
old_ftz_mode: None,
send_sync_marker: PhantomData,
}
}
} else {
Self {
old_ftz_mode: None,
send_sync_marker: PhantomData,
}
}
}
}
}
impl Drop for ScopedFtz {
fn drop(&mut self) {
if let Some(mode) = self.old_ftz_mode {
cfg_if::cfg_if! {
if #[cfg(target_feature = "sse")] {
unsafe { std::arch::x86_64::_MM_SET_FLUSH_ZERO_MODE(mode) };
}
};
}
}
}