File indexing completed on 2024-05-19 04:27:07

0001 /*
0002  * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisSupportedArchitectures.h"
0008 
0009 #include <KConfigGroup>
0010 #include <KSharedConfig>
0011 #include <kis_debug.h>
0012 
0013 #include "xsimd_extensions/xsimd.hpp"
0014 
0015 std::tuple<bool, bool> vectorizationConfiguration()
0016 {
0017     static const std::tuple<bool, bool> vectorization = [&]() {
0018         KConfigGroup cfg = KSharedConfig::openConfig()->group("");
0019         // use the old key name for compatibility
0020         const bool useVectorization =
0021             !cfg.readEntry("amdDisableVectorWorkaround", false);
0022         const bool disableAVXOptimizations =
0023             cfg.readEntry("disableAVXOptimizations", false);
0024 
0025         return std::make_tuple(useVectorization, disableAVXOptimizations);
0026     }();
0027 
0028     return vectorization;
0029 }
0030 
0031 QString KisSupportedArchitectures::baseArchName()
0032 {
0033     return xsimd::current_arch::name();
0034 }
0035 
0036 QString KisSupportedArchitectures::bestArchName()
0037 {
0038 #ifdef HAVE_XSIMD
0039     const unsigned int best_arch = xsimd::available_architectures().best;
0040 
0041 #ifdef Q_PROCESSOR_X86
0042     bool useVectorization = true;
0043     bool disableAVXOptimizations = false;
0044 
0045     std::tie(useVectorization, disableAVXOptimizations) =
0046         vectorizationConfiguration();
0047 
0048     if (!useVectorization) {
0049         qWarning() << "WARNING: vector instructions disabled by the "
0050                       "\'amdDisableVectorWorkaround\' option!";
0051         return "Scalar";
0052     }
0053 
0054     if (disableAVXOptimizations
0055         && (xsimd::avx::version() <= best_arch
0056             || xsimd::avx2::version() <= best_arch)) {
0057         qWarning() << "WARNING: AVX and AVX2 optimizations are disabled by the "
0058                       "\'disableAVXOptimizations\' option!";
0059     }
0060 
0061     /**
0062      * We use SSE2, SSSE3, SSE4.1, AVX and AVX2+FMA.
0063      * The rest are integer and string instructions mostly.
0064      */
0065     if (!disableAVXOptimizations
0066         && xsimd::fma3<xsimd::avx2>::version() <= best_arch) {
0067         return xsimd::fma3<xsimd::avx2>::name();
0068     } else if (!disableAVXOptimizations && xsimd::avx::version() <= best_arch) {
0069         return xsimd::avx::name();
0070     } else if (xsimd::sse4_1::version() <= best_arch) {
0071         return xsimd::sse4_1::name();
0072     } else if (xsimd::ssse3::version() <= best_arch) {
0073         return xsimd::ssse3::name();
0074     } else if (xsimd::sse2::version() <= best_arch) {
0075         return xsimd::sse2::name();
0076     }
0077 #elif XSIMD_WITH_NEON64
0078     if (xsimd::neon64::version() <= best_arch) {
0079         return xsimd::neon64::name();
0080     }
0081 #elif XSIMD_WITH_NEON
0082     if (xsimd::neon::version() <= best_arch) {
0083         return xsimd::neon::name();
0084     }
0085 #endif // XSIMD_WITH_SSE2
0086 #endif // HAVE_XSIMD
0087     return xsimd::current_arch::name();
0088 }
0089 
0090 unsigned int KisSupportedArchitectures::bestArch()
0091 {
0092 #ifdef HAVE_XSIMD
0093     const unsigned int best_arch = xsimd::available_architectures().best;
0094 
0095 #ifdef Q_PROCESSOR_X86
0096     bool useVectorization = true;
0097     bool disableAVXOptimizations = false;
0098 
0099     std::tie(useVectorization, disableAVXOptimizations) =
0100         vectorizationConfiguration();
0101 
0102     if (!useVectorization) {
0103         qWarning() << "WARNING: vector instructions disabled by the "
0104                       "\'amdDisableVectorWorkaround\' option!";
0105         return 0;
0106     }
0107 
0108     if (disableAVXOptimizations
0109         && (xsimd::avx::version() <= best_arch
0110             || xsimd::avx2::version() <= best_arch)) {
0111         qWarning() << "WARNING: AVX and AVX2 optimizations are disabled by the "
0112                       "\'disableAVXOptimizations\' option!";
0113     }
0114 
0115     /**
0116      * We use SSE2, SSSE3, SSE4.1, AVX and AVX2+FMA.
0117      * The rest are integer and string instructions mostly.
0118      */
0119     if (!disableAVXOptimizations
0120         && xsimd::fma3<xsimd::avx2>::version() <= best_arch) {
0121         return xsimd::fma3<xsimd::avx2>::version();
0122     } else if (!disableAVXOptimizations && xsimd::avx::version() <= best_arch) {
0123         return xsimd::avx::version();
0124     } else if (xsimd::sse4_1::version() <= best_arch) {
0125         return xsimd::sse4_1::version();
0126     } else if (xsimd::ssse3::version() <= best_arch) {
0127         return xsimd::ssse3::version();
0128     } else if (xsimd::sse2::version() <= best_arch) {
0129         return xsimd::sse2::version();
0130     }
0131 #elif XSIMD_WITH_NEON64
0132     if (xsimd::neon64::version() <= best_arch) {
0133         return xsimd::neon64::version();
0134     }
0135 #elif XSIMD_WITH_NEON
0136     if (xsimd::neon::version() <= best_arch) {
0137         return xsimd::neon::version();
0138     }
0139 #endif // XSIMD_WITH_SSE2
0140 #endif // HAVE_XSIMD
0141 
0142     return 0;
0143 }
0144 
0145 template<typename S>
0146 struct is_supported_arch {
0147     is_supported_arch(S &log)
0148         : l(log)
0149     {
0150     }
0151 
0152     template<typename A>
0153     void operator()(A) const
0154     {
0155 #ifdef HAVE_XSIMD
0156         if (A::version() <= xsimd::available_architectures().best) {
0157             l.append(A::name()).append(" ");
0158         }
0159 #endif
0160     }
0161 
0162     S &l;
0163 };
0164 
0165 QString KisSupportedArchitectures::supportedInstructionSets()
0166 {
0167     static const QString archs = []() {
0168         QString archs;
0169 #ifdef HAVE_XSIMD
0170         xsimd::all_architectures::for_each(is_supported_arch<QString>{archs});
0171 #endif
0172         return archs;
0173     }();
0174     return archs;
0175 }