Update the read and write SIMD helpers with the new approach

This commit is contained in:
Paul Ferrand 2020-05-30 20:11:27 +02:00 committed by Jean Pierre Cimalando
parent 91435249c0
commit f827e7c2e5
6 changed files with 168 additions and 175 deletions

View file

@ -3,6 +3,7 @@ macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
list (APPEND ${SOURCES_VAR}
${PREFIX}/sfizz/SIMDSSE.cpp
${PREFIX}/sfizz/SIMDHelpers.cpp
${PREFIX}/sfizz/SIMDNEON.cpp
${PREFIX}/sfizz/SIMDDummy.cpp)

View file

@ -60,7 +60,7 @@ void readBaseFile(SndfileHandle& sndFile, sfz::FileAudioBuffer& output, uint32_t
output.clear();
sfz::Buffer<float> tempReadBuffer { 2 * numFrames };
sndFile.readf(tempReadBuffer.data(), numFrames);
sfz::readInterleaved<float>(tempReadBuffer, output.getSpan(0), output.getSpan(1));
sfz::readInterleaved(tempReadBuffer, output.getSpan(0), output.getSpan(1));
}
if (reverse) {
@ -87,7 +87,6 @@ std::unique_ptr<sfz::FileAudioBuffer> readFromFile(SndfileHandle& sndFile, uint3
return outputBuffer;
}
template <class T>
void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, bool reverse, sfz::FileAudioBuffer& output, std::atomic<size_t>* filledFrames = nullptr)
{
if (factor == sfz::Oversampling::x1) {
@ -400,7 +399,7 @@ void sfz::FilePool::loadingThread() noexcept
continue;
}
const auto frames = static_cast<uint32_t>(sndFile.frames());
streamFromFile<float>(sndFile, frames, oversamplingFactor, promise->fileId.isReverse(), promise->fileData, &promise->availableFrames);
streamFromFile(sndFile, frames, oversamplingFactor, promise->fileId.isReverse(), promise->fileData, &promise->availableFrames);
promise->dataStatus = FilePromise::DataStatus::Ready;
const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime;
logger.logFileTime(waitDuration, loadDuration, frames, promise->fileId.filename());

View file

@ -1,12 +1,20 @@
#include "SIMDHelpers.h"
#include <array>
#include "cpuid/cpuinfo.hpp"
namespace sfz{
#include "SIMDConfig.h"
static std::array<bool, static_cast<unsigned>(sfz::SIMDOps::_sentinel)> simdStatus;
#if SFIZZ_HAVE_SSE2
#include <xmmintrin.h>
#include <emmintrin.h>
#endif
namespace sfz {
static std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
static bool simdStatusInitialized = false;
static void resetSIMDStatus()
void resetSIMDStatus()
{
simdStatus[static_cast<unsigned>(SIMDOps::writeInterleaved)] = true;
simdStatus[static_cast<unsigned>(SIMDOps::readInterleaved)] = true;
@ -32,14 +40,15 @@ static void resetSIMDStatus()
simdStatusInitialized = true;
}
static void setSIMDOpStatus(SIMDOps op, bool status)
void setSIMDOpStatus(SIMDOps op, bool status)
{
if (!simdStatusInitialized)
resetSIMDStatus();
simdStatus[static_cast<unsigned>(op)] = status;
}
static bool getSIMDOpStatus(SIMDOps op)
bool getSIMDOpStatus(SIMDOps op)
{
if (!simdStatusInitialized)
resetSIMDStatus();
@ -47,5 +56,85 @@ static bool getSIMDOpStatus(SIMDOps op)
return simdStatus[static_cast<unsigned>(op)];
}
constexpr uintptr_t TypeAlignment = 4;
template<class T>
inline void tickRead(const T*& input, T*& outputLeft, T*& outputRight)
{
*outputLeft++ = *input++;
*outputRight++ = *input++;
}
template <class T>
inline void tickWrite(T*& output, const T*& inputLeft, const T*& inputRight)
{
*output++ = *inputLeft++;
*output++ = *inputRight++;
}
void readInterleaved(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept
{
const auto sentinel = input + inputSize - 1;
cpuid::cpuinfo cpuInfo;
if (getSIMDOpStatus(SIMDOps::readInterleaved)) {
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
if (cpuInfo.has_sse()) {
const auto* lastAligned = prevAligned(input + inputSize - 4);
while (unaligned(input, outputLeft, outputRight) && input < lastAligned)
tickRead(input, outputLeft, outputRight);
while (input < lastAligned) {
auto register0 = _mm_load_ps(input);
auto register1 = _mm_load_ps(input + 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(outputLeft, register0);
_mm_store_ps(outputRight, register1);
incrementAll<4>(input, input, outputLeft, outputRight);
}
// Fallthrough from lastAligned to sentinel
}
#endif
}
while (input < sentinel)
tickRead(input, outputLeft, outputRight);
}
void writeInterleaved(const float* inputLeft, const float* inputRight, float* output, unsigned outputSize) noexcept
{
const auto sentinel = output + outputSize - 1;
cpuid::cpuinfo cpuInfo;
if (getSIMDOpStatus(SIMDOps::readInterleaved)) {
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
if (cpuInfo.has_sse()) {
const auto* lastAligned = prevAligned(output + outputSize - 4);
while (unaligned(output, inputRight, inputLeft) && output < lastAligned)
tickWrite(output, inputLeft, inputRight);
while (output < lastAligned) {
const auto lInRegister = _mm_load_ps(inputLeft);
const auto rInRegister = _mm_load_ps(inputRight);
const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister);
_mm_store_ps(output, outRegister1);
const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister);
_mm_store_ps(output + 4, outRegister2);
incrementAll<4>(output, output, inputLeft, inputRight);
}
// fallthrough from lastAligned to sentinel
}
#endif
}
while (output < sentinel)
tickWrite(output, inputLeft, inputRight);
}
}

View file

@ -69,18 +69,35 @@ enum class SIMDOps {
upsampling,
_sentinel //
};
// Enable or disable SIMD accelerators at runtime
static void setSIMDOpStatus(SIMDOps op, bool status);
static bool getSIMDOpStatus(SIMDOps op);
void setSIMDOpStatus(SIMDOps op, bool status);
bool getSIMDOpStatus(SIMDOps op);
constexpr uintptr_t ByteAlignmentMask { config::defaultAlignment - 1 };
namespace _internals {
template <class T>
inline void snippetRead(const T*& input, T*& outputLeft, T*& outputRight)
{
*outputLeft++ = *input++;
*outputRight++ = *input++;
}
template<class T>
T* nextAligned(const T* ptr)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) + ByteAlignmentMask & (~ByteAlignmentMask));
}
template<class T>
T* prevAligned(const T* ptr)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) & (~ByteAlignmentMask));
}
template<class T>
bool unaligned(const T* ptr)
{
return (reinterpret_cast<uintptr_t>(ptr) & ByteAlignmentMask )!= 0;
}
template<class T, class... Args>
bool unaligned(const T* ptr1, Args... rest)
{
return unaligned(ptr1) || unaligned(rest...);
}
/**
@ -89,33 +106,21 @@ namespace _internals {
* The output size will be the minimum of the input span and output spans size.
*
* @tparam T the underlying type
* @tparam SIMD use the SIMD version or the scalar version
* @param input
* @param outputLeft
* @param outputRight
*/
template <class T, bool SIMD = SIMDConfig::readInterleaved>
void readInterleaved(absl::Span<const T> input, absl::Span<T> outputLeft, absl::Span<T> outputRight) noexcept
void readInterleaved(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept;
inline void readInterleaved(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{
// The size of the output is not big enough for the input...
CHECK(outputLeft.size() >= input.size() / 2);
CHECK(outputRight.size() >= input.size() / 2);
auto* in = input.begin();
auto* lOut = outputLeft.begin();
auto* rOut = outputRight.begin();
while (in < (input.end() - 1) && lOut < outputLeft.end() && rOut < outputRight.end())
_internals::snippetRead<T>(in, lOut, rOut);
const auto size = min(input.size(), 2 * outputLeft.size(), 2 * outputRight.size());
readInterleaved(input.data(), outputLeft.data(), outputRight.data(), size);
}
namespace _internals {
template <class T>
inline void snippetWrite(T*& output, const T*& inputLeft, const T*& inputRight)
{
*output++ = *inputLeft++;
*output++ = *inputRight++;
}
}
/**
* @brief Write a pair of left and right stereo input into a single buffer interleaved.
@ -123,30 +128,21 @@ namespace _internals {
* The output size will be the minimum of the input spans and output span size.
*
* @tparam T the underlying type
* @tparam SIMD use the SIMD version or the scalar version
* @param inputLeft
* @param inputRight
* @param output
*/
template <class T, bool SIMD = SIMDConfig::writeInterleaved>
void writeInterleaved(absl::Span<const T> inputLeft, absl::Span<const T> inputRight, absl::Span<T> output) noexcept
void writeInterleaved(const float* inputLeft, const float* inputRight, float* output, unsigned outputSize) noexcept;
inline void writeInterleaved(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{
CHECK(inputLeft.size() <= output.size() / 2);
CHECK(inputRight.size() <= output.size() / 2);
auto* lIn = inputLeft.begin();
auto* rIn = inputRight.begin();
auto* out = output.begin();
while (lIn < inputLeft.end() && rIn < inputRight.end() && out < (output.end() - 1))
_internals::snippetWrite<T>(out, lIn, rIn);
// Not enough data in the inputs
CHECK(inputLeft.size() >= output.size() / 2);
CHECK(inputRight.size() >= output.size() / 2);
const auto size = min(output.size(), 2 * inputLeft.size(), 2 * inputRight.size());
writeInterleaved(inputLeft.data(), inputRight.data(), output.data(), size);
}
// Specializations
template <>
void writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept;
template <>
void readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept;
/**
* @brief Fill a buffer with a value; comparable to std::fill in essence.
*

View file

@ -12,116 +12,9 @@
#include <array>
#include <xmmintrin.h>
#include <emmintrin.h>
#include "mathfuns/sse_mathfun.h"
using Type = float;
constexpr uintptr_t TypeAlignment { 4 };
constexpr uintptr_t ByteAlignment { TypeAlignment * sizeof(Type) };
constexpr uintptr_t ByteAlignmentMask { ByteAlignment - 1 };
struct AlignmentSentinels {
float* nextAligned;
float* lastAligned;
};
float* nextAligned(const float* ptr)
{
return reinterpret_cast<float*>((reinterpret_cast<uintptr_t>(ptr) + ByteAlignmentMask) & (~ByteAlignmentMask));
}
float* prevAligned(const float* ptr)
{
return reinterpret_cast<float*>(reinterpret_cast<uintptr_t>(ptr) & (~ByteAlignmentMask));
}
bool unaligned(const float* ptr)
{
return (reinterpret_cast<uintptr_t>(ptr) & ByteAlignmentMask) != 0;
}
template<class... Args>
bool unaligned(const float* ptr1, Args... rest)
{
return unaligned(ptr1) || unaligned(rest...);
}
template <>
void sfz::readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{
// The size of the outputs is not big enough for the input...
CHECK(outputLeft.size() >= input.size() / 2);
CHECK(outputRight.size() >= input.size() / 2);
// Input is too small
CHECK(input.size() > 1);
auto* in = input.begin();
auto* lOut = outputLeft.begin();
auto* rOut = outputRight.begin();
const auto size = std::min(input.size(), std::min(outputLeft.size() * 2, outputRight.size() * 2));
const auto* lastAligned = prevAligned(input.begin() + size - TypeAlignment);
while (unaligned(in, lOut, rOut) && in < lastAligned)
_internals::snippetRead<float>(in, lOut, rOut);
while (in < lastAligned) {
auto register0 = _mm_load_ps(in);
in += TypeAlignment;
auto register1 = _mm_load_ps(in);
in += TypeAlignment;
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 += TypeAlignment;
rOut += TypeAlignment;
}
while (in < input.end() - 1)
_internals::snippetRead<float>(in, lOut, rOut);
}
template <>
void sfz::writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{
// The size of the output is not big enough for the inputs...
CHECK(inputLeft.size() <= output.size() / 2);
CHECK(inputRight.size() <= output.size() / 2);
auto* lIn = inputLeft.begin();
auto* rIn = inputRight.begin();
auto* out = output.begin();
const auto size = std::min(output.size(), std::min(inputLeft.size(), inputRight.size()) * 2);
const auto* lastAligned = prevAligned(output.begin() + size - TypeAlignment);
while (unaligned(out, rIn, lIn) && out < lastAligned)
_internals::snippetWrite<float>(out, lIn, rIn);
while (out < lastAligned) {
const auto lInRegister = _mm_load_ps(lIn);
const auto rInRegister = _mm_load_ps(rIn);
const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister);
_mm_store_ps(out, outRegister1);
out += TypeAlignment;
const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister);
_mm_store_ps(out, outRegister2);
out += TypeAlignment;
lIn += TypeAlignment;
rIn += TypeAlignment;
}
while (out < output.end() - 1)
_internals::snippetWrite<float>(out, lIn, rIn);
}
constexpr uintptr_t TypeAlignment = 4;
template <>
void sfz::fill<float, true>(absl::Span<float> output, float value) noexcept

View file

@ -122,7 +122,8 @@ TEST_CASE("[Helpers] Interleaved read")
std::array<float, 16> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput;
std::array<float, 8> rightOutput;
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 16> real;
auto realIdx = 0;
@ -139,7 +140,8 @@ TEST_CASE("[Helpers] Interleaved read unaligned end")
std::array<float, 20> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 10> leftOutput;
std::array<float, 10> rightOutput;
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 20> real;
auto realIdx = 0;
@ -156,7 +158,8 @@ TEST_CASE("[Helpers] Small interleaved read unaligned end")
std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f };
std::array<float, 3> leftOutput;
std::array<float, 3> rightOutput;
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 6> real;
auto realIdx = 0;
@ -173,7 +176,8 @@ TEST_CASE("[Helpers] Interleaved read -- SIMD")
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput;
std::array<float, 8> rightOutput;
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 16> real;
auto realIdx = 0;
@ -190,7 +194,8 @@ TEST_CASE("[Helpers] Interleaved read unaligned end -- SIMD")
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 10> leftOutput;
std::array<float, 10> rightOutput;
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 20> real;
auto realIdx = 0;
@ -207,7 +212,8 @@ TEST_CASE("[Helpers] Small interleaved read unaligned end -- SIMD")
std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f };
std::array<float, 3> leftOutput;
std::array<float, 3> rightOutput;
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 6> real;
auto realIdx = 0;
@ -226,8 +232,10 @@ TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar")
std::array<float, medBufferSize> leftOutputSIMD;
std::array<float, medBufferSize> rightOutputSIMD;
std::iota(input.begin(), input.end(), 0.0f);
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutputScalar), absl::MakeSpan(rightOutputScalar));
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutputSIMD), absl::MakeSpan(rightOutputSIMD));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutputScalar), absl::MakeSpan(rightOutputScalar));
sfz::setSIMDOpStatus(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutputSIMD), absl::MakeSpan(rightOutputSIMD));
REQUIRE(leftOutputScalar == leftOutputSIMD);
REQUIRE(rightOutputScalar == rightOutputSIMD);
}
@ -247,7 +255,8 @@ TEST_CASE("[Helpers] Interleaved write")
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output;
std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -257,7 +266,8 @@ TEST_CASE("[Helpers] Interleaved write unaligned end")
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output;
std::array<float, 20> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -267,7 +277,8 @@ TEST_CASE("[Helpers] Small interleaved write unaligned end")
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output;
std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -286,7 +297,8 @@ TEST_CASE("[Helpers] Interleaved write -- SIMD")
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output;
std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -296,7 +308,7 @@ TEST_CASE("[Helpers] Interleaved write unaligned end -- SIMD")
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output;
std::array<float, 20> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -306,7 +318,8 @@ TEST_CASE("[Helpers] Small interleaved write unaligned end -- SIMD")
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output;
std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -318,8 +331,10 @@ TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar")
std::array<float, medBufferSize * 2> outputSIMD;
std::iota(leftInput.begin(), leftInput.end(), 0.0f);
std::iota(rightInput.begin(), rightInput.end(), static_cast<float>(medBufferSize));
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(outputScalar));
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(outputSIMD));
REQUIRE(outputScalar == outputSIMD);
}