From 627c38dc23f272df6a8ed4352a19a2822974b361 Mon Sep 17 00:00:00 2001
From: Paul Walker <paul@pwjw.com>
Date: Mon, 3 May 2021 12:32:51 -0400
Subject: [PATCH] CPU Info Shenanigans

1. Add the google C++ cpu detection protable code as a submod
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. get popcorn

Closes #4466
---
 .gitmodules                     |  3 ++
 CMakeLists.txt                  | 21 ++++++++-
 libs/cpu_features               |  1 +
 src/common/CPUFeatures.cpp      | 83 +++++++++++++++++++++++++++++++++
 src/common/CPUFeatures.h        | 33 +++++++++++++
 src/common/SurgeSynthesizer.cpp | 11 +++++
 src/gui/CAboutBox.cpp           | 63 +------------------------
 7 files changed, 153 insertions(+), 62 deletions(-)
 create mode 160000 libs/cpu_features
 create mode 100644 src/common/CPUFeatures.cpp
 create mode 100644 src/common/CPUFeatures.h

diff --git a/.gitmodules b/.gitmodules
index 59eaf4583f3..2001bc904f0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,3 +22,6 @@
 [submodule "libs/JUCE"]
 	path = libs/JUCE
 	url = https://github.com/juce-framework/JUCE
+[submodule "libs/cpu_features"]
+	path = libs/cpu_features
+	url = https://github.com/google/cpu_features.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5832cf4bef6..726a937d73f 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)
     $<$<CXX_COMPILER_ID:MSVC>:/MP>
   )
@@ -138,6 +154,7 @@ add_subdirectory(libs/escape-from-vstgui)
 add_subdirectory(libs/oddsound-mts)
 add_subdirectory(libs/libsamplerate EXCLUDE_FROM_ALL)
 add_subdirectory(libs/sqlite-3.23.3)
+add_subdirectory(libs/cpu_features EXCLODE_FROM_ALL)
 
 juce_add_binary_data(surge-shared-binary
         NAMESPACE SurgeCoreBinary
@@ -159,6 +176,7 @@ target_link_libraries(surge-shared PUBLIC
   surge::oddsound-mts
   samplerate
   surge-shared-binary
+  cpu_features
 )
 
 # We want to run this once alas, since JUCE needs it even though it is a byproduct of a phase to build the
@@ -274,6 +292,7 @@ set(SURGE_SHARED_SOURCES
   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/libs/cpu_features b/libs/cpu_features
new file mode 160000
index 00000000000..3e8243b7d99
--- /dev/null
+++ b/libs/cpu_features
@@ -0,0 +1 @@
+Subproject commit 3e8243b7d9951c078259c3186c039a6e8f036055
diff --git a/src/common/CPUFeatures.cpp b/src/common/CPUFeatures.cpp
new file mode 100644
index 00000000000..97a5c230e55
--- /dev/null
+++ b/src/common/CPUFeatures.cpp
@@ -0,0 +1,83 @@
+/*
+** 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 ARM_NEON
+#else
+#include "cpuinfo_x86.h"
+#endif
+
+using namespace cpu_features;
+
+namespace Surge
+{
+namespace CPUFeatures
+{
+#if ARM_NEON
+#else
+static const X86Features features = GetX86Info().features;
+#endif
+
+std::string cpuBrand()
+{
+#if ARM_NEON
+    return "ARM Processor";
+#else
+    char bs[49];
+    FillX86BrandString(bs);
+    return bs;
+#endif
+}
+
+bool isArm()
+{
+#if ARM_NEON
+    return true;
+#else
+    return false;
+#endif
+}
+bool isX86()
+{
+#if ARM_NEON
+    return false;
+#else
+    return true;
+#endif
+}
+bool hasSSE2()
+{
+#if ARM_NEON
+    return true; // thanks simde
+#else
+    return features.sse2;
+#endif
+}
+bool hasAVX()
+{
+#if ARM_NEON
+    return true; // thanks simde
+#else
+    return features.avx;
+#endif
+}
+
+#if !CPU_FEATURES_COMPILED_X86_AVX
+#error "You must compile SURGE with AVX support in compiler flags"
+#endif
+} // namespace CPUFeatures
+} // namespace Surge
\ No newline at end of file
diff --git a/src/common/CPUFeatures.h b/src/common/CPUFeatures.h
new file mode 100644
index 00000000000..687d280cbda
--- /dev/null
+++ b/src/common/CPUFeatures.h
@@ -0,0 +1,33 @@
+/*
+** 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 <string>
+
+namespace Surge
+{
+namespace CPUFeatures
+{
+std::string cpuBrand();
+bool isArm();
+bool isX86();
+bool hasSSE2();
+bool hasAVX();
+}; // 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 <ctime>
+#include "CPUFeatures.h"
 #if MAC || LINUX
 #include <pthread.h>
 #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/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