diff --git a/ricepp/CMakeLists.txt b/ricepp/CMakeLists.txt index 8b80dfe6..cde27fe4 100644 --- a/ricepp/CMakeLists.txt +++ b/ricepp/CMakeLists.txt @@ -78,7 +78,7 @@ foreach(target ${RICEPP_LIBS_CPUSPECIFIC}) list(APPEND RICEPP_OBJECT_TARGETS ${target}) endforeach() -add_library(ricepp-core OBJECT ricepp.cpp) +add_library(ricepp-core OBJECT ricepp.cpp cpu_variant.cpp) # target_link_libraries(ricepp-core PUBLIC range-v3::range-v3) target_include_directories(ricepp-core SYSTEM PUBLIC $> diff --git a/ricepp/cpu_variant.cpp b/ricepp/cpu_variant.cpp new file mode 100644 index 00000000..4367c7d1 --- /dev/null +++ b/ricepp/cpu_variant.cpp @@ -0,0 +1,76 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of ricepp. + * + * ricepp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ricepp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ricepp. If not, see . + */ + +#include +#include + +#include "cpu_variant.h" + +namespace ricepp::detail { + +namespace { + +detail::cpu_variant get_cpu_variant_init() { +#ifndef _WIN32 +#if defined(__has_builtin) +#if __has_builtin(__builtin_cpu_supports) && \ + (defined(RICEPP_CPU_BMI2) || defined(RICEPP_CPU_BMI2_AVX512)) + __builtin_cpu_init(); + + bool const has_avx512vl = __builtin_cpu_supports("avx512vl"); + bool const has_avx512vbmi = __builtin_cpu_supports("avx512vbmi"); + bool const has_bmi2 = __builtin_cpu_supports("bmi2"); + + if (has_avx512vl && has_avx512vbmi && has_bmi2) { + return detail::cpu_variant::has_bmi2_avx512; + } + + if (has_bmi2) { + return detail::cpu_variant::has_bmi2; + } +#endif +#endif +#endif + + return detail::cpu_variant::fallback; +} + +} // namespace + +detail::cpu_variant get_cpu_variant() { + static detail::cpu_variant const variant = get_cpu_variant_init(); + return variant; +} + +void show_cpu_variant(std::string_view variant) { + if (std::getenv("RICEPP_SHOW_CPU_VARIANT")) { + std::cerr << "ricepp: using " << variant << " CPU variant\n"; + } +} + +void show_cpu_variant_once(std::string_view variant) { + static auto const _ = [&variant]() { + show_cpu_variant(variant); + return true; + }(); +} + +} // namespace ricepp::detail diff --git a/ricepp/cpu_variant.h b/ricepp/cpu_variant.h new file mode 100644 index 00000000..a8b4f043 --- /dev/null +++ b/ricepp/cpu_variant.h @@ -0,0 +1,38 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of ricepp. + * + * ricepp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ricepp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ricepp. If not, see . + */ + +#pragma once + +#include + +namespace ricepp::detail { + +enum class cpu_variant { + fallback, + has_bmi2, + has_bmi2_avx512, +}; + +cpu_variant get_cpu_variant(); +void show_cpu_variant(std::string_view variant); +void show_cpu_variant_once(std::string_view variant); + +} // namespace ricepp::detail diff --git a/ricepp/include/ricepp/ricepp.h b/ricepp/include/ricepp/codec_config.h similarity index 81% rename from ricepp/include/ricepp/ricepp.h rename to ricepp/include/ricepp/codec_config.h index 5474148c..f55a1f17 100644 --- a/ricepp/include/ricepp/ricepp.h +++ b/ricepp/include/ricepp/codec_config.h @@ -22,12 +22,7 @@ #pragma once #include -#include -#include -#include -#include - -#include +#include namespace ricepp { @@ -38,8 +33,4 @@ struct codec_config { unsigned unused_lsb_count; }; -template -std::unique_ptr> -create_codec(codec_config const& config); - } // namespace ricepp diff --git a/ricepp/include/ricepp/create_decoder.h b/ricepp/include/ricepp/create_decoder.h new file mode 100644 index 00000000..07b0db3c --- /dev/null +++ b/ricepp/include/ricepp/create_decoder.h @@ -0,0 +1,36 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of ricepp. + * + * ricepp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ricepp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ricepp. If not, see . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace ricepp { + +template +std::unique_ptr> +create_decoder(codec_config const& config); + +} // namespace ricepp diff --git a/ricepp/include/ricepp/create_encoder.h b/ricepp/include/ricepp/create_encoder.h new file mode 100644 index 00000000..e153d8e1 --- /dev/null +++ b/ricepp/include/ricepp/create_encoder.h @@ -0,0 +1,36 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of ricepp. + * + * ricepp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ricepp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ricepp. If not, see . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace ricepp { + +template +std::unique_ptr> +create_encoder(codec_config const& config); + +} // namespace ricepp diff --git a/ricepp/include/ricepp/decoder_interface.h b/ricepp/include/ricepp/decoder_interface.h new file mode 100644 index 00000000..c442e848 --- /dev/null +++ b/ricepp/include/ricepp/decoder_interface.h @@ -0,0 +1,41 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of ricepp. + * + * ricepp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ricepp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ricepp. If not, see . + */ + +#pragma once + +#include +#include +#include + +namespace ricepp { + +template +class decoder_interface { + public: + using pixel_type = PixelT; + + virtual ~decoder_interface() = default; + + virtual void decode(std::span output, + std::span input) const = 0; +}; + +} // namespace ricepp diff --git a/ricepp/include/ricepp/codec_interface.h b/ricepp/include/ricepp/encoder_interface.h similarity index 88% rename from ricepp/include/ricepp/codec_interface.h rename to ricepp/include/ricepp/encoder_interface.h index 107138f6..5ec1bde9 100644 --- a/ricepp/include/ricepp/codec_interface.h +++ b/ricepp/include/ricepp/encoder_interface.h @@ -22,17 +22,18 @@ #pragma once #include +#include #include #include namespace ricepp { template -class codec_interface { +class encoder_interface { public: using pixel_type = PixelT; - virtual ~codec_interface() = default; + virtual ~encoder_interface() = default; [[nodiscard]] virtual std::vector encode(std::span input) const = 0; @@ -45,9 +46,6 @@ class codec_interface { virtual std::span encode(std::span output, std::span input) const = 0; - - virtual void decode(std::span output, - std::span input) const = 0; }; } // namespace ricepp diff --git a/ricepp/ricepp.cpp b/ricepp/ricepp.cpp index 6e5d62dc..6961bf70 100644 --- a/ricepp/ricepp.cpp +++ b/ricepp/ricepp.cpp @@ -19,92 +19,25 @@ * along with ricepp. If not, see . */ -#include -#include -#include -#include -#include - -#include +#include +#include #include "ricepp_cpuspecific.h" namespace ricepp { -namespace { - -detail::cpu_variant get_cpu_variant_init() { -#ifndef _WIN32 -#if defined(__has_builtin) -#if __has_builtin(__builtin_cpu_supports) && \ - (defined(RICEPP_CPU_BMI2) || defined(RICEPP_CPU_BMI2_AVX512)) - __builtin_cpu_init(); - - bool const has_avx512vl = __builtin_cpu_supports("avx512vl"); - bool const has_avx512vbmi = __builtin_cpu_supports("avx512vbmi"); - bool const has_bmi2 = __builtin_cpu_supports("bmi2"); - - if (has_avx512vl && has_avx512vbmi && has_bmi2) { - return detail::cpu_variant::has_bmi2_avx512; - } - - if (has_bmi2) { - return detail::cpu_variant::has_bmi2; - } -#endif -#endif -#endif - - return detail::cpu_variant::fallback; +template <> +std::unique_ptr> +create_encoder(codec_config const& config) { + return detail::create_codec_cpuspecific< + encoder_interface, detail::encoder_cpuspecific_, uint16_t>(config); } -detail::cpu_variant get_cpu_variant() { - static detail::cpu_variant const variant = get_cpu_variant_init(); - return variant; -} - -void show_cpu_variant(std::string_view variant) { - if (std::getenv("RICEPP_SHOW_CPU_VARIANT")) { - std::cerr << "ricepp: using " << variant << " CPU variant\n"; - } -} - -void show_cpu_variant_once(std::string_view variant) { - static auto const _ = [&variant]() { - show_cpu_variant(variant); - return true; - }(); -} - -} // namespace - template <> -std::unique_ptr> -create_codec(codec_config const& config) { - switch (get_cpu_variant()) { -#ifdef RICEPP_CPU_BMI2_AVX512 - case detail::cpu_variant::has_bmi2_avx512: - show_cpu_variant_once("BMI2+AVX512"); - return detail::create_codec_cpuspecific_< - uint16_t, detail::cpu_variant::has_bmi2_avx512>(config); -#endif - -#ifdef RICEPP_CPU_BMI2 - case detail::cpu_variant::has_bmi2: - show_cpu_variant_once("BMI2"); - return detail::create_codec_cpuspecific_( - config); -#endif - - default: - show_cpu_variant_once("fallback"); - return detail::create_codec_cpuspecific_( - config); - } - - throw std::runtime_error("internal error: unknown CPU variant"); +std::unique_ptr> +create_decoder(codec_config const& config) { + return detail::create_codec_cpuspecific< + decoder_interface, detail::decoder_cpuspecific_, uint16_t>(config); } } // namespace ricepp diff --git a/ricepp/ricepp_benchmark.cpp b/ricepp/ricepp_benchmark.cpp index 778a21f0..d4358720 100644 --- a/ricepp/ricepp_benchmark.cpp +++ b/ricepp/ricepp_benchmark.cpp @@ -25,7 +25,8 @@ #include #include -#include +#include +#include namespace { @@ -76,19 +77,23 @@ class ricepp_bm : public ::benchmark::Fixture { .full_freq = 1.0 / state.range(5), }); - codec_ = ricepp::create_codec({ + auto config = ricepp::codec_config{ .block_size = static_cast(state.range(6)), .component_stream_count = static_cast(state.range(7)), .byteorder = state.range(1) ? std::endian::big : std::endian::little, .unused_lsb_count = static_cast(state.range(2)), - }); + }; - encoded_ = codec_->encode(data_); + encoder_ = ricepp::create_encoder(config); + decoder_ = ricepp::create_decoder(config); + + encoded_ = encoder_->encode(data_); } void TearDown(::benchmark::State const&) {} - std::unique_ptr> codec_; + std::unique_ptr> encoder_; + std::unique_ptr> decoder_; std::vector data_; std::vector encoded_; }; @@ -119,9 +124,9 @@ void ricepp_params(benchmark::internal::Benchmark* b) { BENCHMARK_DEFINE_F(ricepp_bm, encode)(::benchmark::State& state) { std::vector encoded; - encoded.resize(codec_->worst_case_encoded_bytes(data_)); + encoded.resize(encoder_->worst_case_encoded_bytes(data_)); for (auto _ : state) { - auto r = codec_->encode(encoded, data_); + auto r = encoder_->encode(encoded, data_); ::benchmark::DoNotOptimize(r); } state.SetBytesProcessed(static_cast(state.iterations()) * @@ -132,7 +137,7 @@ BENCHMARK_DEFINE_F(ricepp_bm, decode)(::benchmark::State& state) { std::vector decoded; decoded.resize(data_.size()); for (auto _ : state) { - codec_->decode(decoded, encoded_); + decoder_->decode(decoded, encoded_); } state.SetBytesProcessed(static_cast(state.iterations()) * data_.size() * sizeof(data_[0])); diff --git a/ricepp/ricepp_benchmark_fits.cpp b/ricepp/ricepp_benchmark_fits.cpp index d3c02b3a..92ce3d9b 100644 --- a/ricepp/ricepp_benchmark_fits.cpp +++ b/ricepp/ricepp_benchmark_fits.cpp @@ -30,7 +30,8 @@ #include #include -#include +#include +#include namespace { @@ -72,12 +73,15 @@ class ricepp_bm : public ::benchmark::Fixture { auto const& camera_info = g_camera_info.at(camera); - codec_ = ricepp::create_codec({ + auto config = ricepp::codec_config{ .block_size = 128, .component_stream_count = camera_info.component_stream_count, .byteorder = std::endian::big, .unused_lsb_count = camera_info.unused_lsb_count, - }); + }; + + encoder_ = ricepp::create_encoder(config); + decoder_ = ricepp::create_decoder(config); if (data_.empty()) { std::filesystem::path testdata_dir; @@ -89,7 +93,7 @@ class ricepp_bm : public ::benchmark::Fixture { data_ = load_fits_data(testdata_dir / camera / (test + ".fit")); - encoded_ = codec_->encode(data_); + encoded_ = encoder_->encode(data_); } latch_->count_down(); @@ -124,7 +128,8 @@ class ricepp_bm : public ::benchmark::Fixture { return data; } - std::unique_ptr> codec_; + std::unique_ptr> encoder_; + std::unique_ptr> decoder_; std::vector data_; std::vector encoded_; std::optional latch_{1}; @@ -136,9 +141,9 @@ class ricepp_bm : public ::benchmark::Fixture { BENCHMARK_DEFINE_F(ricepp_bm, encode_##camera##_##test) \ (::benchmark::State & state) { \ thread_local std::vector encoded; \ - encoded.resize(codec_->worst_case_encoded_bytes(data_)); \ + encoded.resize(encoder_->worst_case_encoded_bytes(data_)); \ for (auto _ : state) { \ - auto r = codec_->encode(encoded, data_); \ + auto r = encoder_->encode(encoded, data_); \ ::benchmark::DoNotOptimize(r); \ } \ state.SetBytesProcessed(static_cast(state.iterations()) * \ @@ -151,7 +156,7 @@ class ricepp_bm : public ::benchmark::Fixture { thread_local std::vector decoded; \ decoded.resize(data_.size()); \ for (auto _ : state) { \ - codec_->decode(decoded, encoded_); \ + decoder_->decode(decoded, encoded_); \ } \ state.SetBytesProcessed(static_cast(state.iterations()) * \ data_.size() * sizeof(data_[0])); \ diff --git a/ricepp/ricepp_cpuspecific.cpp b/ricepp/ricepp_cpuspecific.cpp index db0c7e87..e8660d27 100644 --- a/ricepp/ricepp_cpuspecific.cpp +++ b/ricepp/ricepp_cpuspecific.cpp @@ -19,110 +19,27 @@ * along with ricepp. If not, see . */ -#include -#include - -#include - #include #include -#include #include -#include -#include -#include "ricepp_cpuspecific.h" +#include "ricepp_cpuspecific_traits.h" -namespace ricepp { +namespace ricepp::detail { namespace { -template -class dynamic_pixel_traits { - public: - using value_type = ValueType; - static constexpr size_t const kBitCount = - std::numeric_limits::digits; - static constexpr value_type const kAllOnes = - std::numeric_limits::max(); - - dynamic_pixel_traits(std::endian byteorder, - unsigned unused_lsb_count) noexcept - : unused_lsb_count_{unused_lsb_count} - , byteorder_{byteorder} -#ifndef NDEBUG - , lsb_mask_{static_cast(~(kAllOnes << unused_lsb_count))} - , msb_mask_{static_cast(~(kAllOnes >> unused_lsb_count))} -#endif - { - assert(unused_lsb_count < kBitCount); - } - - [[nodiscard]] RICEPP_FORCE_INLINE value_type - read(value_type value) const noexcept { - value_type tmp = byteswap(value, byteorder_); - assert((tmp & lsb_mask_) == 0); - return tmp >> unused_lsb_count_; - } - - [[nodiscard]] RICEPP_FORCE_INLINE value_type - write(value_type value) const noexcept { - assert((value & msb_mask_) == 0); - return byteswap(static_cast(value << unused_lsb_count_), - byteorder_); - } - - private: - unsigned const unused_lsb_count_; - std::endian const byteorder_; -#ifndef NDEBUG - value_type const lsb_mask_; - value_type const msb_mask_; -#endif -}; - -template -class static_pixel_traits { - public: - using value_type = ValueType; - static constexpr size_t const kBitCount = - std::numeric_limits::digits; - static constexpr value_type const kAllOnes = - std::numeric_limits::max(); - static constexpr std::endian const kByteOrder = ByteOrder; - static constexpr unsigned const kUnusedLsbCount = UnusedLsbCount; - static constexpr value_type const kLsbMask = - static_cast(~(kAllOnes << kUnusedLsbCount)); - static constexpr value_type const kMsbMask = - static_cast(~(kAllOnes >> kUnusedLsbCount)); - static_assert(kUnusedLsbCount < kBitCount); - - [[nodiscard]] static RICEPP_FORCE_INLINE value_type - read(value_type value) noexcept { - value_type tmp = byteswap(value); - assert((tmp & kLsbMask) == 0); - return tmp >> kUnusedLsbCount; - } - - [[nodiscard]] static RICEPP_FORCE_INLINE value_type - write(value_type value) noexcept { - assert((value & kMsbMask) == 0); - return byteswap( - static_cast(value << kUnusedLsbCount)); - } -}; - template -class codec_impl final - : public codec_interface, +class encoder_impl final + : public encoder_interface, public PixelTraits { public: using pixel_type = typename PixelTraits::value_type; - using codec_type = codec; + using codec_type = + ricepp::codec; - codec_impl(PixelTraits const& traits, size_t block_size) + encoder_impl(PixelTraits const& traits, size_t block_size) : PixelTraits{traits} , block_size_{block_size} {} @@ -147,11 +64,6 @@ class codec_impl final input.size()); } - void decode(std::span output, - std::span input) const override { - decode_impl(output.data(), output.size(), input.data(), input.size()); - } - private: size_t worst_case_encoded_bytes_impl(codec_type& codec, size_t size) const { return (codec.worst_case_bit_count(size) + 8 - 1) / 8; @@ -169,12 +81,6 @@ class codec_impl final std::span{input, input_size}); } - void decode_impl(pixel_type* __restrict output, size_t output_size, - uint8_t const* __restrict input, size_t input_size) const { - return decode_impl(std::span{output, output_size}, - std::span{input, input_size}); - } - std::vector encode_impl(std::span input) const { std::vector output; codec_type codec{block_size_, *this}; @@ -194,6 +100,35 @@ class codec_impl final return std::span{output.begin(), writer.iterator()}; } + private: + size_t const block_size_; +}; + +template +class decoder_impl final + : public decoder_interface, + public PixelTraits { + public: + using pixel_type = typename PixelTraits::value_type; + using codec_type = codec; + + decoder_impl(PixelTraits const& traits, size_t block_size) + : PixelTraits{traits} + , block_size_{block_size} {} + + void decode(std::span output, + std::span input) const override { + decode_impl(output.data(), output.size(), input.data(), input.size()); + } + + private: + void decode_impl(pixel_type* __restrict output, size_t output_size, + uint8_t const* __restrict input, size_t input_size) const { + return decode_impl(std::span{output, output_size}, + std::span{input, input_size}); + } + void decode_impl(std::span output, std::span input) const { bitstream_reader reader{input.begin(), input.end()}; @@ -205,90 +140,30 @@ class codec_impl final size_t const block_size_; }; -template -std::unique_ptr> -create_codec_(size_t block_size, PixelTraits const& traits) { - if (block_size <= 512) { - return std::make_unique>( - traits, block_size); - } - - return nullptr; -} - -template -std::unique_ptr> -create_codec_(size_t block_size, size_t component_stream_count, - PixelTraits const& traits) { - switch (component_stream_count) { - case 1: - return create_codec_<1, PixelTraits>(block_size, traits); - - case 2: - return create_codec_<2, PixelTraits>(block_size, traits); - - default: - break; - } - - return nullptr; -} - -template -std::unique_ptr> -create_codec_(size_t block_size, size_t component_stream_count) { - using pixel_traits = - static_pixel_traits; - - if (auto codec = create_codec_( - block_size, component_stream_count, pixel_traits{})) { - return codec; - } - - return nullptr; -} - -template -std::unique_ptr> -create_codec_(codec_config const& config) { - if (config.byteorder == std::endian::big) { - switch (config.unused_lsb_count) { - case 0: - return create_codec_( - config.block_size, config.component_stream_count); - - case 2: - return create_codec_( - config.block_size, config.component_stream_count); - - case 4: - return create_codec_( - config.block_size, config.component_stream_count); - } - } - - using pixel_traits = dynamic_pixel_traits; - - return create_codec_( - config.block_size, config.component_stream_count, - pixel_traits{config.byteorder, config.unused_lsb_count}); -} - } // namespace -namespace detail { - template <> -std::unique_ptr> -create_codec_cpuspecific_( +std::unique_ptr> +encoder_cpuspecific_::create( codec_config const& config) { - if (auto codec = create_codec_(config)) { - return codec; + if (auto encoder = + create_codec_(config)) { + return encoder; } throw std::runtime_error("Unsupported configuration"); } -} // namespace detail -} // namespace ricepp +template <> +std::unique_ptr> +decoder_cpuspecific_::create( + codec_config const& config) { + if (auto decoder = + create_codec_(config)) { + return decoder; + } + + throw std::runtime_error("Unsupported configuration"); +} + +} // namespace ricepp::detail diff --git a/ricepp/ricepp_cpuspecific.h b/ricepp/ricepp_cpuspecific.h index 6827c233..9d3eeaa3 100644 --- a/ricepp/ricepp_cpuspecific.h +++ b/ricepp/ricepp_cpuspecific.h @@ -23,8 +23,13 @@ #include #include +#include +#include -#include +#include +#include + +#include "cpu_variant.h" namespace ricepp { @@ -32,15 +37,46 @@ struct codec_config; namespace detail { -enum class cpu_variant { - fallback, - has_bmi2, - has_bmi2_avx512, +template