mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 07:13:02 +04:00
Merge pull request #7462 from alalek:cpu_multi_target
This commit is contained in:
+259
-40
@@ -237,24 +237,81 @@ void Exception::formatMessage()
|
||||
msg = format("%s:%d: error: (%d) %s\n", file.c_str(), line, code, err.c_str());
|
||||
}
|
||||
|
||||
static const char* g_hwFeatureNames[CV_HARDWARE_MAX_FEATURE] = { NULL };
|
||||
|
||||
static const char* getHWFeatureName(int id)
|
||||
{
|
||||
return (id < CV_HARDWARE_MAX_FEATURE) ? g_hwFeatureNames[id] : NULL;
|
||||
}
|
||||
static const char* getHWFeatureNameSafe(int id)
|
||||
{
|
||||
const char* name = getHWFeatureName(id);
|
||||
return name ? name : "Unknown feature";
|
||||
}
|
||||
|
||||
struct HWFeatures
|
||||
{
|
||||
enum { MAX_FEATURE = CV_HARDWARE_MAX_FEATURE };
|
||||
|
||||
HWFeatures(void)
|
||||
HWFeatures(bool run_initialize = false)
|
||||
{
|
||||
memset( have, 0, sizeof(have) );
|
||||
x86_family = 0;
|
||||
memset( have, 0, sizeof(have[0]) * MAX_FEATURE );
|
||||
if (run_initialize)
|
||||
initialize();
|
||||
}
|
||||
|
||||
static HWFeatures initialize(void)
|
||||
static void initializeNames()
|
||||
{
|
||||
HWFeatures f;
|
||||
for (int i = 0; i < CV_HARDWARE_MAX_FEATURE; i++)
|
||||
{
|
||||
g_hwFeatureNames[i] = 0;
|
||||
}
|
||||
g_hwFeatureNames[CPU_MMX] = "MMX";
|
||||
g_hwFeatureNames[CPU_SSE] = "SSE";
|
||||
g_hwFeatureNames[CPU_SSE2] = "SSE2";
|
||||
g_hwFeatureNames[CPU_SSE3] = "SSE3";
|
||||
g_hwFeatureNames[CPU_SSSE3] = "SSSE3";
|
||||
g_hwFeatureNames[CPU_SSE4_1] = "SSE4.1";
|
||||
g_hwFeatureNames[CPU_SSE4_2] = "SSE4.2";
|
||||
g_hwFeatureNames[CPU_POPCNT] = "POPCNT";
|
||||
g_hwFeatureNames[CPU_FP16] = "FP16";
|
||||
g_hwFeatureNames[CPU_AVX] = "AVX";
|
||||
g_hwFeatureNames[CPU_AVX2] = "AVX2";
|
||||
g_hwFeatureNames[CPU_FMA3] = "FMA3";
|
||||
|
||||
g_hwFeatureNames[CPU_AVX_512F] = "AVX512F";
|
||||
g_hwFeatureNames[CPU_AVX_512BW] = "AVX512BW";
|
||||
g_hwFeatureNames[CPU_AVX_512CD] = "AVX512CD";
|
||||
g_hwFeatureNames[CPU_AVX_512DQ] = "AVX512DQ";
|
||||
g_hwFeatureNames[CPU_AVX_512ER] = "AVX512ER";
|
||||
g_hwFeatureNames[CPU_AVX_512IFMA512] = "AVX512IFMA";
|
||||
g_hwFeatureNames[CPU_AVX_512PF] = "AVX512PF";
|
||||
g_hwFeatureNames[CPU_AVX_512VBMI] = "AVX512VBMI";
|
||||
g_hwFeatureNames[CPU_AVX_512VL] = "AVX512VL";
|
||||
|
||||
g_hwFeatureNames[CPU_NEON] = "NEON";
|
||||
}
|
||||
|
||||
void initialize(void)
|
||||
{
|
||||
#ifndef WINRT
|
||||
if (getenv("OPENCV_DUMP_CONFIG"))
|
||||
{
|
||||
fprintf(stderr, "\nOpenCV build configuration is:\n%s\n",
|
||||
cv::getBuildInformation().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
initializeNames();
|
||||
|
||||
int cpuid_data[4] = { 0, 0, 0, 0 };
|
||||
int cpuid_data_ex[4] = { 0, 0, 0, 0 };
|
||||
|
||||
#if defined _MSC_VER && (defined _M_IX86 || defined _M_X64)
|
||||
#define OPENCV_HAVE_X86_CPUID 1
|
||||
__cpuid(cpuid_data, 1);
|
||||
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
||||
#define OPENCV_HAVE_X86_CPUID 1
|
||||
#ifdef __x86_64__
|
||||
asm __volatile__
|
||||
(
|
||||
@@ -278,33 +335,36 @@ struct HWFeatures
|
||||
#endif
|
||||
#endif
|
||||
|
||||
f.x86_family = (cpuid_data[0] >> 8) & 15;
|
||||
if( f.x86_family >= 6 )
|
||||
#ifdef OPENCV_HAVE_X86_CPUID
|
||||
int x86_family = (cpuid_data[0] >> 8) & 15;
|
||||
if( x86_family >= 6 )
|
||||
{
|
||||
f.have[CV_CPU_MMX] = (cpuid_data[3] & (1 << 23)) != 0;
|
||||
f.have[CV_CPU_SSE] = (cpuid_data[3] & (1<<25)) != 0;
|
||||
f.have[CV_CPU_SSE2] = (cpuid_data[3] & (1<<26)) != 0;
|
||||
f.have[CV_CPU_SSE3] = (cpuid_data[2] & (1<<0)) != 0;
|
||||
f.have[CV_CPU_SSSE3] = (cpuid_data[2] & (1<<9)) != 0;
|
||||
f.have[CV_CPU_FMA3] = (cpuid_data[2] & (1<<12)) != 0;
|
||||
f.have[CV_CPU_SSE4_1] = (cpuid_data[2] & (1<<19)) != 0;
|
||||
f.have[CV_CPU_SSE4_2] = (cpuid_data[2] & (1<<20)) != 0;
|
||||
f.have[CV_CPU_POPCNT] = (cpuid_data[2] & (1<<23)) != 0;
|
||||
f.have[CV_CPU_AVX] = (((cpuid_data[2] & (1<<28)) != 0)&&((cpuid_data[2] & (1<<27)) != 0));//OS uses XSAVE_XRSTORE and CPU support AVX
|
||||
f.have[CV_CPU_FP16] = (cpuid_data[2] & (1<<29)) != 0;
|
||||
have[CV_CPU_MMX] = (cpuid_data[3] & (1<<23)) != 0;
|
||||
have[CV_CPU_SSE] = (cpuid_data[3] & (1<<25)) != 0;
|
||||
have[CV_CPU_SSE2] = (cpuid_data[3] & (1<<26)) != 0;
|
||||
have[CV_CPU_SSE3] = (cpuid_data[2] & (1<<0)) != 0;
|
||||
have[CV_CPU_SSSE3] = (cpuid_data[2] & (1<<9)) != 0;
|
||||
have[CV_CPU_FMA3] = (cpuid_data[2] & (1<<12)) != 0;
|
||||
have[CV_CPU_SSE4_1] = (cpuid_data[2] & (1<<19)) != 0;
|
||||
have[CV_CPU_SSE4_2] = (cpuid_data[2] & (1<<20)) != 0;
|
||||
have[CV_CPU_POPCNT] = (cpuid_data[2] & (1<<23)) != 0;
|
||||
have[CV_CPU_AVX] = (cpuid_data[2] & (1<<28)) != 0;
|
||||
have[CV_CPU_FP16] = (cpuid_data[2] & (1<<29)) != 0;
|
||||
|
||||
// make the second call to the cpuid command in order to get
|
||||
// information about extended features like AVX2
|
||||
#if defined _MSC_VER && (defined _M_IX86 || defined _M_X64)
|
||||
__cpuidex(cpuid_data, 7, 0);
|
||||
#define OPENCV_HAVE_X86_CPUID_EX 1
|
||||
__cpuidex(cpuid_data_ex, 7, 0);
|
||||
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
||||
#define OPENCV_HAVE_X86_CPUID_EX 1
|
||||
#ifdef __x86_64__
|
||||
asm __volatile__
|
||||
(
|
||||
"movl $7, %%eax\n\t"
|
||||
"movl $0, %%ecx\n\t"
|
||||
"cpuid\n\t"
|
||||
:[eax]"=a"(cpuid_data[0]),[ebx]"=b"(cpuid_data[1]),[ecx]"=c"(cpuid_data[2]),[edx]"=d"(cpuid_data[3])
|
||||
:[eax]"=a"(cpuid_data_ex[0]),[ebx]"=b"(cpuid_data_ex[1]),[ecx]"=c"(cpuid_data_ex[2]),[edx]"=d"(cpuid_data_ex[3])
|
||||
:
|
||||
: "cc"
|
||||
);
|
||||
@@ -317,29 +377,76 @@ struct HWFeatures
|
||||
"cpuid\n\t"
|
||||
"movl %%ebx, %0\n\t"
|
||||
"popl %%ebx\n\t"
|
||||
: "=r"(cpuid_data[1]), "=c"(cpuid_data[2])
|
||||
: "=r"(cpuid_data_ex[1]), "=c"(cpuid_data_ex[2])
|
||||
:
|
||||
: "cc"
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
f.have[CV_CPU_AVX2] = (cpuid_data[1] & (1<<5)) != 0;
|
||||
|
||||
f.have[CV_CPU_AVX_512F] = (cpuid_data[1] & (1<<16)) != 0;
|
||||
f.have[CV_CPU_AVX_512DQ] = (cpuid_data[1] & (1<<17)) != 0;
|
||||
f.have[CV_CPU_AVX_512IFMA512] = (cpuid_data[1] & (1<<21)) != 0;
|
||||
f.have[CV_CPU_AVX_512PF] = (cpuid_data[1] & (1<<26)) != 0;
|
||||
f.have[CV_CPU_AVX_512ER] = (cpuid_data[1] & (1<<27)) != 0;
|
||||
f.have[CV_CPU_AVX_512CD] = (cpuid_data[1] & (1<<28)) != 0;
|
||||
f.have[CV_CPU_AVX_512BW] = (cpuid_data[1] & (1<<30)) != 0;
|
||||
f.have[CV_CPU_AVX_512VL] = (cpuid_data[1] & (1<<31)) != 0;
|
||||
f.have[CV_CPU_AVX_512VBMI] = (cpuid_data[2] & (1<<1)) != 0;
|
||||
#ifdef OPENCV_HAVE_X86_CPUID_EX
|
||||
have[CV_CPU_AVX2] = (cpuid_data_ex[1] & (1<<5)) != 0;
|
||||
|
||||
have[CV_CPU_AVX_512F] = (cpuid_data_ex[1] & (1<<16)) != 0;
|
||||
have[CV_CPU_AVX_512DQ] = (cpuid_data_ex[1] & (1<<17)) != 0;
|
||||
have[CV_CPU_AVX_512IFMA512] = (cpuid_data_ex[1] & (1<<21)) != 0;
|
||||
have[CV_CPU_AVX_512PF] = (cpuid_data_ex[1] & (1<<26)) != 0;
|
||||
have[CV_CPU_AVX_512ER] = (cpuid_data_ex[1] & (1<<27)) != 0;
|
||||
have[CV_CPU_AVX_512CD] = (cpuid_data_ex[1] & (1<<28)) != 0;
|
||||
have[CV_CPU_AVX_512BW] = (cpuid_data_ex[1] & (1<<30)) != 0;
|
||||
have[CV_CPU_AVX_512VL] = (cpuid_data_ex[1] & (1<<31)) != 0;
|
||||
have[CV_CPU_AVX_512VBMI] = (cpuid_data_ex[2] & (1<<1)) != 0;
|
||||
#else
|
||||
CV_UNUSED(cpuid_data_ex);
|
||||
#endif
|
||||
|
||||
bool have_AVX_OS_support = true;
|
||||
bool have_AVX512_OS_support = true;
|
||||
if (!(cpuid_data[2] & (1<<27)))
|
||||
have_AVX_OS_support = false; // OS uses XSAVE_XRSTORE and CPU support AVX
|
||||
else
|
||||
{
|
||||
int xcr0 = 0;
|
||||
#ifdef _XCR_XFEATURE_ENABLED_MASK // requires immintrin.h
|
||||
xcr0 = (int)_xgetbv(_XCR_XFEATURE_ENABLED_MASK);
|
||||
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
||||
__asm__ ("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx" );
|
||||
#endif
|
||||
if ((xcr0 & 0x6) != 0x6)
|
||||
have_AVX_OS_support = false; // YMM registers
|
||||
if ((xcr0 & 0xe6) != 0xe6)
|
||||
have_AVX512_OS_support = false; // ZMM registers
|
||||
}
|
||||
|
||||
if (!have_AVX_OS_support)
|
||||
{
|
||||
have[CV_CPU_AVX] = false;
|
||||
have[CV_CPU_FP16] = false;
|
||||
have[CV_CPU_AVX2] = false;
|
||||
have[CV_CPU_FMA3] = false;
|
||||
}
|
||||
if (!have_AVX_OS_support || !have_AVX512_OS_support)
|
||||
{
|
||||
have[CV_CPU_AVX_512F] = false;
|
||||
have[CV_CPU_AVX_512BW] = false;
|
||||
have[CV_CPU_AVX_512CD] = false;
|
||||
have[CV_CPU_AVX_512DQ] = false;
|
||||
have[CV_CPU_AVX_512ER] = false;
|
||||
have[CV_CPU_AVX_512IFMA512] = false;
|
||||
have[CV_CPU_AVX_512PF] = false;
|
||||
have[CV_CPU_AVX_512VBMI] = false;
|
||||
have[CV_CPU_AVX_512VL] = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
CV_UNUSED(cpuid_data);
|
||||
CV_UNUSED(cpuid_data_ex);
|
||||
#endif // OPENCV_HAVE_X86_CPUID
|
||||
|
||||
#if defined ANDROID || defined __linux__
|
||||
#ifdef __aarch64__
|
||||
f.have[CV_CPU_NEON] = true;
|
||||
f.have[CV_CPU_FP16] = true;
|
||||
have[CV_CPU_NEON] = true;
|
||||
have[CV_CPU_FP16] = true;
|
||||
#elif defined __arm__
|
||||
int cpufile = open("/proc/self/auxv", O_RDONLY);
|
||||
|
||||
@@ -352,8 +459,8 @@ struct HWFeatures
|
||||
{
|
||||
if (auxv.a_type == AT_HWCAP)
|
||||
{
|
||||
f.have[CV_CPU_NEON] = (auxv.a_un.a_val & 4096) != 0;
|
||||
f.have[CV_CPU_FP16] = (auxv.a_un.a_val & 2) != 0;
|
||||
have[CV_CPU_NEON] = (auxv.a_un.a_val & 4096) != 0;
|
||||
have[CV_CPU_FP16] = (auxv.a_un.a_val & 2) != 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -363,21 +470,133 @@ struct HWFeatures
|
||||
#endif
|
||||
#elif (defined __clang__ || defined __APPLE__)
|
||||
#if (defined __ARM_NEON__ || (defined __ARM_NEON && defined __aarch64__))
|
||||
f.have[CV_CPU_NEON] = true;
|
||||
have[CV_CPU_NEON] = true;
|
||||
#endif
|
||||
#if (defined __ARM_FP && (((__ARM_FP & 0x2) != 0) && defined __ARM_NEON__))
|
||||
f.have[CV_CPU_FP16] = true;
|
||||
have[CV_CPU_FP16] = true;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return f;
|
||||
int baseline_features[] = { CV_CPU_BASELINE_FEATURES };
|
||||
if (!checkFeatures(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0])))
|
||||
{
|
||||
fprintf(stderr, "\n"
|
||||
"******************************************************************\n"
|
||||
"* FATAL ERROR: *\n"
|
||||
"* This OpenCV build doesn't support current CPU/HW configuration *\n"
|
||||
"* *\n"
|
||||
"* Use OPENCV_DUMP_CONFIG=1 environment variable for details *\n"
|
||||
"******************************************************************\n");
|
||||
fprintf(stderr, "\nRequired baseline features:\n");
|
||||
checkFeatures(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0]), true);
|
||||
CV_ErrorNoReturn(cv::Error::StsAssert, "Missing support for required CPU baseline features. Check OpenCV build configuration and required CPU/HW setup.");
|
||||
}
|
||||
|
||||
readSettings(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0]));
|
||||
}
|
||||
|
||||
bool checkFeatures(const int* features, int count, bool dump = false)
|
||||
{
|
||||
bool result = true;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int feature = features[i];
|
||||
if (feature)
|
||||
{
|
||||
if (have[feature])
|
||||
{
|
||||
if (dump) fprintf(stderr, "%s - OK\n", getHWFeatureNameSafe(feature));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
if (dump) fprintf(stderr, "%s - NOT AVAILABLE\n", getHWFeatureNameSafe(feature));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool isSymbolSeparator(char c)
|
||||
{
|
||||
return c == ',' || c == ';' || c == '-';
|
||||
}
|
||||
|
||||
void readSettings(const int* baseline_features, int baseline_count)
|
||||
{
|
||||
bool dump = true;
|
||||
const char* disabled_features =
|
||||
#ifndef WINRT
|
||||
getenv("OPENCV_CPU_DISABLE");
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
if (disabled_features && disabled_features[0] != 0)
|
||||
{
|
||||
const char* start = disabled_features;
|
||||
for (;;)
|
||||
{
|
||||
while (start[0] != 0 && isSymbolSeparator(start[0]))
|
||||
{
|
||||
start++;
|
||||
}
|
||||
if (start[0] == 0)
|
||||
break;
|
||||
const char* end = start;
|
||||
while (end[0] != 0 && !isSymbolSeparator(end[0]))
|
||||
{
|
||||
end++;
|
||||
}
|
||||
if (end == start)
|
||||
continue;
|
||||
cv::String feature(start, end);
|
||||
start = end;
|
||||
|
||||
CV_Assert(feature.size() > 0);
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < CV_HARDWARE_MAX_FEATURE; i++)
|
||||
{
|
||||
if (!g_hwFeatureNames[i]) continue;
|
||||
size_t len = strlen(g_hwFeatureNames[i]);
|
||||
if (len != feature.size()) continue;
|
||||
if (feature.compare(g_hwFeatureNames[i]) == 0)
|
||||
{
|
||||
bool isBaseline = false;
|
||||
for (int k = 0; k < baseline_count; k++)
|
||||
{
|
||||
if (baseline_features[k] == i)
|
||||
{
|
||||
isBaseline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isBaseline)
|
||||
{
|
||||
if (dump) fprintf(stderr, "OPENCV: Trying to disable baseline CPU feature: '%s'. This has very limited effect, because code optimizations for this feature are executed unconditionally in the most cases.\n", getHWFeatureNameSafe(i));
|
||||
}
|
||||
if (!have[i])
|
||||
{
|
||||
if (dump) fprintf(stderr, "OPENCV: Trying to disable unavailable CPU feature on the current platform: '%s'.\n", getHWFeatureNameSafe(i));
|
||||
}
|
||||
have[i] = false;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
if (dump) fprintf(stderr, "OPENCV: Trying to disable unknown CPU feature: '%s'.\n", feature.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int x86_family;
|
||||
bool have[MAX_FEATURE+1];
|
||||
};
|
||||
|
||||
static HWFeatures featuresEnabled = HWFeatures::initialize(), featuresDisabled = HWFeatures();
|
||||
static HWFeatures featuresEnabled(true), featuresDisabled = HWFeatures(false);
|
||||
static HWFeatures* currentFeatures = &featuresEnabled;
|
||||
|
||||
bool checkHardwareSupport(int feature)
|
||||
|
||||
Reference in New Issue
Block a user