From a5388b46da2efc7e360d3d1c25e6bdfad1b23d9c Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 3 May 2021 13:57:38 -0400 Subject: [PATCH] CPU Info Shenanigans (#4468) 1. Add CPU detection 2. Turn on AVX flags at build time 3. If you are running on a non-AVX box pop a warning saying may not work in the future 4. move the FPUState stuff and reinstate it in XT while at it Closes #4466 --- CMakeLists.txt | 20 +- src/common/CPUFeatures.cpp | 206 +++++++++++++++++++ src/common/CPUFeatures.h | 41 ++++ src/common/SurgeSynthesizer.cpp | 11 + src/common/util/FpuState.cpp | 35 ---- src/common/util/FpuState.h | 13 -- src/gui/CAboutBox.cpp | 63 +----- src/surge_synth_juce/SurgeSynthProcessor.cpp | 3 + 8 files changed, 281 insertions(+), 111 deletions(-) create mode 100644 src/common/CPUFeatures.cpp create mode 100644 src/common/CPUFeatures.h delete mode 100644 src/common/util/FpuState.cpp delete mode 100644 src/common/util/FpuState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5832cf4bef6..27b811a0c68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,21 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") add_compile_options(-msse2 -mfpmath=sse) endif() endif() + + # Add AVX support + include(CheckCXXSourceCompiles) + check_cxx_source_compiles(" +#if defined(__x86_64__) || defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) + #ifndef __AVX__ + #error + #endif +#endif + int main() {}" COMPILER_HAS_AVX_OR_IS_ARM) + if (NOT COMPILER_HAS_AVX_OR_IS_ARM) + message(STATUS "Adding -mavx to allow __AVX__ compiles" ) + add_compile_options("-mavx") + endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Any Clang add_compile_options( @@ -76,7 +91,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") endif() if(MSVC) - # MSVC and pretenders add_compile_options( -WX # treat all warnings as errors @@ -97,6 +111,8 @@ if(MSVC) /Zc:alignedNew /bigobj + /arch:AVX + # Build with Multiple Processes (Clang-cl builds use Ninja instead) $<$:/MP> ) @@ -269,11 +285,11 @@ set(SURGE_SHARED_SOURCES src/common/dsp/Wavetable.cpp src/common/dsp/WavetableOscillator.cpp src/common/dsp/WindowOscillator.cpp - src/common/util/FpuState.cpp src/common/vt_dsp/basic_dsp.cpp src/common/vt_dsp/halfratefilter.cpp src/common/vt_dsp/lipol.cpp src/common/vt_dsp/macspecific.cpp + src/common/CPUFeatures.cpp src/common/DebugHelpers.cpp src/common/ModulatorPresetManager.cpp src/common/Parameter.cpp diff --git a/src/common/CPUFeatures.cpp b/src/common/CPUFeatures.cpp new file mode 100644 index 00000000000..1ba111b6cc8 --- /dev/null +++ b/src/common/CPUFeatures.cpp @@ -0,0 +1,206 @@ +/* +** Surge Synthesizer is Free and Open Source Software +** +** Surge is made available under the Gnu General Public License, v3.0 +** https://www.gnu.org/licenses/gpl-3.0.en.html +** +** Copyright 2004-2021 by various individuals as described by the Git transaction log +** +** All source at: https://github.com/surge-synthesizer/surge.git +** +** Surge was a commercial product from 2004-2018, with Copyright and ownership +** in that period held by Claes Johanson at Vember Audio. Claes made Surge +** open source in September 2018. +*/ + +#include "CPUFeatures.h" +#include "globals.h" + +#if MAC +#include +#include +#endif + +#if LINUX +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +namespace Surge +{ +namespace CPUFeatures +{ + +#if LINUX && !ARM_NEON +#ifdef __GNUC__ +// Thanks to https://gist.github.com/hi2p-perim/7855506 +void __cpuid(int *cpuinfo, int info) +{ + __asm__ __volatile__("xchg %%ebx, %%edi;" + "cpuid;" + "xchg %%ebx, %%edi;" + : "=a"(cpuinfo[0]), "=D"(cpuinfo[1]), "=c"(cpuinfo[2]), "=d"(cpuinfo[3]) + : "0"(info)); +} + +unsigned long long _xgetbv(unsigned int index) +{ + unsigned int eax, edx; + __asm__ __volatile__("xgetbv;" : "=a"(eax), "=d"(edx) : "c"(index)); + return ((unsigned long long)edx << 32) | eax; +} + +#endif +#endif + +std::string cpuBrand() +{ + std::string arch = "Unknown CPU"; +#if MAC + + char buffer[1024]; + size_t bufsz = sizeof(buffer); + if (sysctlbyname("machdep.cpu.brand_string", (void *)(&buffer), &bufsz, nullptr, (size_t)0) < 0) + { +#if ARM_NEON + arch = "Apple Silicon"; +#endif + } + else + { + arch = buffer; +#if ARM_NEON + arch += " (Apple Silicon)"; +#endif + } + +#elif WINDOWS + std::string platform = "Windows"; + + int CPUInfo[4] = {-1}; + unsigned nExIds, i = 0; + char CPUBrandString[0x40]; + // Get the information associated with each extended ID. + __cpuid(CPUInfo, 0x80000000); + nExIds = CPUInfo[0]; + for (i = 0x80000000; i <= nExIds; ++i) + { + __cpuid(CPUInfo, i); + // Interpret CPU brand string + if (i == 0x80000002) + memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); + else if (i == 0x80000003) + memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); + else if (i == 0x80000004) + memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); + } + arch = CPUBrandString; +#elif LINUX + std::string platform = "Linux"; + + // Lets see what /proc/cpuinfo has to say for us + // on intels this is "model name" + auto pinfo = std::ifstream("/proc/cpuinfo"); + if (pinfo.is_open()) + { + std::string line; + while (std::getline(pinfo, line)) + { + if (line.find("model name") == 0) + { + auto colon = line.find(":"); + arch = line.substr(colon + 1); + break; + } + if (line.find("Model") == 0) // rasperry pi branch + { + auto colon = line.find(":"); + arch = line.substr(colon + 1); + break; + } + } + } + pinfo.close(); +#endif + return arch; +} + +bool isArm() +{ +#if ARM_NEON + return true; +#else + return false; +#endif +} +bool isX86() +{ +#if ARM_NEON + return false; +#else + return true; +#endif +} +bool hasSSE2() { return true; } +bool hasAVX() +{ +#if ARM_NEON + return true; // thanks simde +#else +#if MAC + return true; +#endif +#if WINDOWS || LINUX + bool avxSup; + int cpuinfo[4]; + __cpuid(cpuinfo, 1); + + avxSup = cpuinfo[2] & (1 << 28) || false; + bool osxsaveSup = cpuinfo[2] & (1 << 27) || false; + if (osxsaveSup && avxSup) + { + // _XCR_XFEATURE_ENABLED_MASK = 0 + unsigned long long xcrFeatureMask = _xgetbv(0); + avxSup = (xcrFeatureMask & 0x6) == 0x6; + } + return avxSup; +#endif + +#endif +} + +#if !(__AVX__ || ARM_NEON) +#error "You must compile SURGE with AVX support in compiler flags" +#endif + +FPUStateGuard::FPUStateGuard() +{ +#ifndef ARM_NEON + auto _SSE_Flags = 0x8040; + bool fpuExceptions = false; + + priorS = _mm_getcsr(); + if (fpuExceptions) + { + _mm_setcsr(((priorS & ~_MM_MASK_MASK) | _SSE_Flags) | _MM_EXCEPT_MASK); // all on + } + else + { + _mm_setcsr((priorS | _SSE_Flags) | _MM_MASK_MASK); + } + + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif +} + +FPUStateGuard::~FPUStateGuard() +{ +#ifndef ARM_NEON + _mm_setcsr(priorS); +#endif +} +} // namespace CPUFeatures +} // namespace Surge diff --git a/src/common/CPUFeatures.h b/src/common/CPUFeatures.h new file mode 100644 index 00000000000..53004c93579 --- /dev/null +++ b/src/common/CPUFeatures.h @@ -0,0 +1,41 @@ +/* +** Surge Synthesizer is Free and Open Source Software +** +** Surge is made available under the Gnu General Public License, v3.0 +** https://www.gnu.org/licenses/gpl-3.0.en.html +** +** Copyright 2004-2021 by various individuals as described by the Git transaction log +** +** All source at: https://github.com/surge-synthesizer/surge.git +** +** Surge was a commercial product from 2004-2018, with Copyright and ownership +** in that period held by Claes Johanson at Vember Audio. Claes made Surge +** open source in September 2018. +*/ + +#ifndef SURGE_XT_CPUFEATURES_H +#define SURGE_XT_CPUFEATURES_H + +#include + +namespace Surge +{ +namespace CPUFeatures +{ +std::string cpuBrand(); +bool isArm(); +bool isX86(); +bool hasSSE2(); +bool hasAVX(); + +struct FPUStateGuard +{ + FPUStateGuard(); + ~FPUStateGuard(); + + int priorS; +}; +}; // namespace CPUFeatures +} // namespace Surge + +#endif // SURGE_XT_CPUFEATURES_H diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index 3c3a4db9302..04b51d4d0ae 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -16,6 +16,7 @@ #include "SurgeSynthesizer.h" #include "DspUtilities.h" #include +#include "CPUFeatures.h" #if MAC || LINUX #include #else @@ -38,6 +39,16 @@ SurgeSynthesizer::SurgeSynthesizer(PluginLayer *parent, std::string suppliedData : storage(suppliedDataPath), hpA(&storage), hpB(&storage), _parent(parent), halfbandA(6, true), halfbandB(6, true), halfbandIN(6, true) { + // Remember CPU features works on ARM also + if (!Surge::CPUFeatures::hasAVX()) + { + storage.reportError( + "Surge XT in the future will require processor with AVX extensions. Surge Xt may" + " not work on this hardware in future versions. Enjoy Surge 1.9!", + "CPU Incompatability"); + // Try anyway + } + switch_toggled_queued = false; audio_processing_active = false; halt_engine = false; diff --git a/src/common/util/FpuState.cpp b/src/common/util/FpuState.cpp deleted file mode 100644 index 2c10a058812..00000000000 --- a/src/common/util/FpuState.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "globals.h" -#include "FpuState.h" -#ifndef ARM_NEON -#include -#endif -#include - -FpuState::FpuState() : _old_SSE_state(0), _SSE_Flags(0x8040) {} - -void FpuState::set() -{ -#ifndef ARM_NEON - bool fpuExceptions = false; - - _old_SSE_state = _mm_getcsr(); - if (fpuExceptions) - { - _mm_setcsr(((_old_SSE_state & ~_MM_MASK_MASK) | _SSE_Flags) | _MM_EXCEPT_MASK); // all on - } - else - { - _mm_setcsr((_old_SSE_state | _SSE_Flags) | _MM_MASK_MASK); - } - // FTZ/DAZ + ignore all exceptions (1 means ignored) - - _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); -#endif -} - -void FpuState::restore() -{ -#ifndef ARM_NEON - _mm_setcsr(_old_SSE_state); -#endif -} diff --git a/src/common/util/FpuState.h b/src/common/util/FpuState.h deleted file mode 100644 index e47b3cf0c41..00000000000 --- a/src/common/util/FpuState.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -class FpuState -{ - public: - FpuState(); - - void set(); - void restore(); - - int _old_SSE_state; - int _SSE_Flags; -}; diff --git a/src/gui/CAboutBox.cpp b/src/gui/CAboutBox.cpp index debb51d921c..fa626ada080 100644 --- a/src/gui/CAboutBox.cpp +++ b/src/gui/CAboutBox.cpp @@ -10,6 +10,7 @@ #include "SurgeGUIEditor.h" #include "CScalableBitmap.h" #include "CTextButtonWithHover.h" +#include "CPUFeatures.h" #if WINDOWS // For __cpuid so we can get processor name @@ -189,73 +190,13 @@ CAboutBox::CAboutBox(const CRect &size, SurgeGUIEditor *editor, SurgeStorage *st std::string flavor = wrapperType; - std::string arch = std::string(Surge::Build::BuildArch); + std::string arch = Surge::CPUFeatures::cpuBrand(); #if MAC std::string platform = "macOS"; - - char buffer[1024]; - size_t bufsz = sizeof(buffer); - if (sysctlbyname("machdep.cpu.brand_string", (void *)(&buffer), &bufsz, nullptr, (size_t)0) < 0) - { -#if ARM_NEON - arch = "Apple Silicon"; -#endif - } - else - { - arch = buffer; -#if ARM_NEON - arch += " (Apple Silicon)"; -#endif - } - #elif WINDOWS std::string platform = "Windows"; - - int CPUInfo[4] = {-1}; - unsigned nExIds, i = 0; - char CPUBrandString[0x40]; - // Get the information associated with each extended ID. - __cpuid(CPUInfo, 0x80000000); - nExIds = CPUInfo[0]; - for (i = 0x80000000; i <= nExIds; ++i) - { - __cpuid(CPUInfo, i); - // Interpret CPU brand string - if (i == 0x80000002) - memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); - else if (i == 0x80000003) - memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); - else if (i == 0x80000004) - memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); - } - arch = CPUBrandString; #elif LINUX std::string platform = "Linux"; - - // Lets see what /proc/cpuinfo has to say for us - // on intels this is "model name" - auto pinfo = std::ifstream("/proc/cpuinfo"); - if (pinfo.is_open()) - { - std::string line; - while (std::getline(pinfo, line)) - { - if (line.find("model name") == 0) - { - auto colon = line.find(":"); - arch = line.substr(colon + 1); - break; - } - if (line.find("Model") == 0) // rasperry pi branch - { - auto colon = line.find(":"); - arch = line.substr(colon + 1); - break; - } - } - } - pinfo.close(); #else std::string platform = "GLaDOS, Orac or Skynet"; #endif diff --git a/src/surge_synth_juce/SurgeSynthProcessor.cpp b/src/surge_synth_juce/SurgeSynthProcessor.cpp index ac9a92d6321..8eb5f9bbd22 100644 --- a/src/surge_synth_juce/SurgeSynthProcessor.cpp +++ b/src/surge_synth_juce/SurgeSynthProcessor.cpp @@ -13,6 +13,7 @@ #include "DebugHelpers.h" #include "SurgeSynthFlavorExtensions.h" #include "version.h" +#include "CPUFeatures.h" using namespace juce; @@ -146,6 +147,8 @@ bool SurgeSynthProcessor::isBusesLayoutSupported(const BusesLayout &layouts) con void SurgeSynthProcessor::processBlock(AudioBuffer &buffer, MidiBuffer &midiMessages) { + auto fpuguard = Surge::CPUFeatures::FPUStateGuard(); + // FIXME obvioulsy float thisBPM = 120.0; auto playhead = getPlayHead();