From 1b81997c57959f2c35a528f852815663d072e49b Mon Sep 17 00:00:00 2001 From: paul Date: Sun, 4 Aug 2019 22:42:48 +0200 Subject: [PATCH] Refactor the SIMD once and for all, hopefully... --- CMakeLists.txt | 31 +++++++++++------ sources/FilePool.h | 4 ++- sources/Globals.h | 17 +++------- sources/Intrinsics.h | 11 ++++++ sources/Parser.cpp | 13 +++++-- sources/Parser.h | 3 ++ sources/StereoBuffer.h | 67 ++++++------------------------------- sources/StereoBufferSSE.cpp | 59 ++++++++++++++++++++++++++++++++ 8 files changed, 121 insertions(+), 84 deletions(-) create mode 100644 sources/Intrinsics.h create mode 100644 sources/StereoBufferSSE.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b12c6098..15666e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,20 +32,12 @@ endif() message("Cmake flags: ${CMAKE_CXX_FLAGS}") -# Check SIMD files +# Check SIMD +set(USE_SIMD OFF) include(CheckIncludeFiles) CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H) CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H) CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_H) -if (HAVE_X86INTRIN_H AND UNIX) - add_compile_options(-DHAVE_X86INTRIN_H) -endif() -if (HAVE_INTRIN_H AND WIN32) - add_compile_options(/DHAVE_INTRIN_H) -endif() -if (HAVE_ARM_NEON_H AND UNIX) - add_compile_options(-DHAVE_ARM_NEON_H) -endif() # Build options and includes set(BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests") @@ -81,6 +73,25 @@ set(COMMON_SOURCES sources/Region.cpp sources/Parser.cpp ) + +set(SSE_SOURCES + sources/StereoBufferSSE.cpp +) + +if (HAVE_X86INTRIN_H AND UNIX) + add_compile_options(-DHAVE_X86INTRIN_H) + add_compile_options(-DUSE_SIMD) + set(COMMON_SOURCES ${COMMON_SOURCES} ${SSE_SOURCES}) +endif() +if (HAVE_INTRIN_H AND WIN32) + add_compile_options(/DHAVE_INTRIN_H) + add_compile_options(/DUSE_SIMD) + set(COMMON_SOURCES ${COMMON_SOURCES} ${SSE_SOURCES}) +endif() +if (HAVE_ARM_NEON_H AND UNIX) + add_compile_options(-DUSE_SIMD) + add_compile_options(-DHAVE_ARM_NEON_H) +endif() set(TEST_SOURCES tests/RegexT.cpp diff --git a/sources/FilePool.h b/sources/FilePool.h index e961cfb4..3adb1739 100644 --- a/sources/FilePool.h +++ b/sources/FilePool.h @@ -36,8 +36,10 @@ public: SndfileHandle sndFile ( reinterpret_cast(file.c_str()) ); FileInformation returnedValue; returnedValue.end = static_cast(sndFile.frames()); + SF_INSTRUMENT instrumentInfo; sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); + if (instrumentInfo.loop_count == 1) { returnedValue.loopBegin = instrumentInfo.loops[0].start; @@ -46,7 +48,7 @@ public: auto preloadedSize = std::min(returnedValue.end, static_cast(config::preloadSize)); returnedValue.preloadedData = std::make_shared>(preloadedSize); sndFile.readf(tempReadBuffer.data(), preloadedSize); - returnedValue.preloadedData->readInterleaved(tempReadBuffer.data(), preloadedSize); + returnedValue.preloadedData->readInterleaved(tempReadBuffer.data(), preloadedSize); preloadedData[filename] = returnedValue.preloadedData; // char buffer [2048] ; // sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ; diff --git a/sources/Globals.h b/sources/Globals.h index 19611c3e..335ee7bb 100644 --- a/sources/Globals.h +++ b/sources/Globals.h @@ -20,21 +20,12 @@ namespace config } // namespace sfz -enum class SIMD { scalar, sse, neon }; namespace SIMDConfig { inline constexpr unsigned int defaultAlignment { 16 }; -#if HAVE_X86INTRIN_H || HAVE_INTRIN_H - inline constexpr SIMD supported { SIMD::sse }; +#if USE_SIMD + inline constexpr bool useSIMD { true }; #else - inline constexpr SIMD supported {SIMD::scalar}; + inline constexpr bool useSIMD { false }; #endif -} // namespace config - -#if HAVE_X86INTRIN_H -#include -#endif - -#if HAVE_INTRIN_H -#include -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/sources/Intrinsics.h b/sources/Intrinsics.h new file mode 100644 index 00000000..fb89449e --- /dev/null +++ b/sources/Intrinsics.h @@ -0,0 +1,11 @@ +#if HAVE_X86INTRIN_H +#include +#endif + +#if HAVE_INTRIN_H +#include +#endif + +#if HAVE_ARM_NEON_H +#include +#endif \ No newline at end of file diff --git a/sources/Parser.cpp b/sources/Parser.cpp index d66cab8b..71146e21 100644 --- a/sources/Parser.cpp +++ b/sources/Parser.cpp @@ -84,10 +84,17 @@ void sfz::Parser::readSfzFile(const std::filesystem::path& fileName, std::vector std::replace(includePath.begin(), includePath.end(), '\\', '/'); const auto newFile = rootDirectory / includePath; auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile); - if (std::filesystem::exists(newFile) && alreadyIncluded == includedFiles.end()) + if (std::filesystem::exists(newFile)) { - includedFiles.push_back(newFile); - readSfzFile(newFile, lines); + if (alreadyIncluded == includedFiles.end()) + { + includedFiles.push_back(newFile); + readSfzFile(newFile, lines); + } + else if (!recursiveIncludeGuard) + { + readSfzFile(newFile, lines); + } } continue; } diff --git a/sources/Parser.h b/sources/Parser.h index 8f5d1cd8..a72cc930 100644 --- a/sources/Parser.h +++ b/sources/Parser.h @@ -24,10 +24,13 @@ public: virtual bool loadSfzFile(const std::filesystem::path& file); const std::map& getDefines() const noexcept { return defines; } const std::vector& getIncludedFiles() const noexcept { return includedFiles; } + void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; } + void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; } protected: virtual void callback(std::string_view header, std::vector members) = 0; std::filesystem::path rootDirectory { std::filesystem::current_path() }; private: + bool recursiveIncludeGuard { false }; std::map defines; std::vector includedFiles; std::string aggregatedContent { }; diff --git a/sources/StereoBuffer.h b/sources/StereoBuffer.h index bffc935a..ed7e94f9 100644 --- a/sources/StereoBuffer.h +++ b/sources/StereoBuffer.h @@ -46,14 +46,14 @@ public: } } - template + template void fill(Type value) noexcept { std::fill(begin(Channel::left), end(Channel::left), value); std::fill(begin(Channel::right), end(Channel::right), value); } - template + template void readInterleaved(Type* input, int numFrames) noexcept { auto* in = input; @@ -70,61 +70,6 @@ public: } } - template <> - void readInterleaved(float *input, int numFrames) noexcept - { - ASSERT(this->numFrames >= numFrames); - const int residualFrames = numFrames & (2 * TypeAlignment - 1); - const int lastAligned = numFrames - residualFrames; - float *in = input; - auto [lOut, rOut] = getChannels(); - const float *end = input + 2 * lastAligned; - while (in < end) - { - auto register0 = _mm_loadu_ps(in); - in += 4; - auto register1 = _mm_loadu_ps(in); - in += 4; - auto register2 = register0; - // register 2 holds the copy of register 0 that is going to get erased by the first operation - // Remember that the bit mask reads from the end; 10 00 10 00 means - // "take 0 from a, take 2 from a, take 0 from b, take 2 from b" - register0 = _mm_shuffle_ps(register0, register1, 0b10001000); - register1 = _mm_shuffle_ps(register2, register1, 0b11011101); - _mm_store_ps(lOut, register0); - _mm_store_ps(rOut, register1); - lOut += 4; - rOut += 4; - } - end = input + numChannels * numFrames; - while (in < end) - { - *lOut = *in; - in++; - *rOut = *in; - in++; - lOut += 1; - rOut += 1; - } - } - - template<> - void fill(float value) noexcept - { - const __m128 mmValue = _mm_set_ps1(value); - auto [lBegin, rBegin] = getChannels(); - auto [lEnd, rEnd] = alignedEnds(); - auto mmLeft = reinterpret_cast<__m128 *>(lBegin); - auto mmRight = reinterpret_cast<__m128 *>(rBegin); - while (mmLeft < reinterpret_cast<__m128 *>(lEnd)) // we should only need to test a single channel - { - _mm_store_ps(reinterpret_cast(mmLeft), mmValue); - _mm_store_ps(reinterpret_cast(mmRight), mmValue); - mmLeft++; - mmRight++; - } - } - Type* getChannel(Channel channel) noexcept { switch(channel) @@ -189,3 +134,11 @@ private: Buffer rightBuffer {}; Type trash { 0 }; }; + +template <> +template <> +void StereoBuffer::readInterleaved(float *input, int numFrames) noexcept; + +template <> +template <> +void StereoBuffer::fill(float value) noexcept; \ No newline at end of file diff --git a/sources/StereoBufferSSE.cpp b/sources/StereoBufferSSE.cpp new file mode 100644 index 00000000..d494b9a5 --- /dev/null +++ b/sources/StereoBufferSSE.cpp @@ -0,0 +1,59 @@ +#include "StereoBuffer.h" +#include "Intrinsics.h" + +template <> +template <> +void StereoBuffer::readInterleaved(float *input, int numFrames) noexcept +{ + ASSERT(this->numFrames >= numFrames); + const int residualFrames = numFrames & (2 * TypeAlignment - 1); + const int lastAligned = numFrames - residualFrames; + float *in = input; + auto [lOut, rOut] = getChannels(); + const float *end = input + 2 * lastAligned; + while (in < end) + { + auto register0 = _mm_loadu_ps(in); + in += 4; + auto register1 = _mm_loadu_ps(in); + in += 4; + auto register2 = register0; + // register 2 holds the copy of register 0 that is going to get erased by the first operation + // Remember that the bit mask reads from the end; 10 00 10 00 means + // "take 0 from a, take 2 from a, take 0 from b, take 2 from b" + register0 = _mm_shuffle_ps(register0, register1, 0b10001000); + register1 = _mm_shuffle_ps(register2, register1, 0b11011101); + _mm_store_ps(lOut, register0); + _mm_store_ps(rOut, register1); + lOut += 4; + rOut += 4; + } + end = input + numChannels * numFrames; + while (in < end) + { + *lOut = *in; + in++; + *rOut = *in; + in++; + lOut += 1; + rOut += 1; + } +} + +template<> +template<> +void StereoBuffer::fill(float value) noexcept +{ + const __m128 mmValue = _mm_set_ps1(value); + auto [lBegin, rBegin] = getChannels(); + auto [lEnd, rEnd] = alignedEnds(); + auto mmLeft = reinterpret_cast<__m128 *>(lBegin); + auto mmRight = reinterpret_cast<__m128 *>(rBegin); + while (mmLeft < reinterpret_cast<__m128 *>(lEnd)) // we should only need to test a single channel + { + _mm_store_ps(reinterpret_cast(mmLeft), mmValue); + _mm_store_ps(reinterpret_cast(mmRight), mmValue); + mmLeft++; + mmRight++; + } +} \ No newline at end of file