diff --git a/3rdparty/cpufeatures/CMakeLists.txt b/3rdparty/cpufeatures/CMakeLists.txt new file mode 100644 index 0000000000..327f2348b6 --- /dev/null +++ b/3rdparty/cpufeatures/CMakeLists.txt @@ -0,0 +1,29 @@ + +ocv_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +file(GLOB cpuf_s *.c) +file(GLOB cpuf_h *.h) + +set(lib_srcs ${cpuf_s}) +set(lib_hdrs ${cpuf_h}) + +set(CPUFEATURES_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") +set(CPUFEATURES_LIBRARIES cpufeatures CACHE INTERNAL "") + +add_library(cpufeatures STATIC ${lib_srcs} ${lib_hdrs}) + +set_target_properties(cpufeatures + PROPERTIES OUTPUT_NAME cpufeatures + DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" + COMPILE_PDB_NAME cpufeatures + COMPILE_PDB_NAME_DEBUG "cpufeatures${OPENCV_DEBUG_POSTFIX}" + ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH} + ) + +if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(cpufeatures PROPERTIES FOLDER "3rdparty") +endif() + +if(NOT BUILD_SHARED_LIBS) + ocv_install_target(cpufeatures EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) +endif() + diff --git a/3rdparty/libwebp/cpu-features/cpu-features.c b/3rdparty/cpufeatures/cpu-features.c similarity index 66% rename from 3rdparty/libwebp/cpu-features/cpu-features.c rename to 3rdparty/cpufeatures/cpu-features.c index 119c2ac057..d8c8e1f365 100644 --- a/3rdparty/libwebp/cpu-features/cpu-features.c +++ b/3rdparty/cpufeatures/cpu-features.c @@ -27,6 +27,10 @@ */ /* ChangeLog for this library: + * + * NDK r10e?: Add MIPS MSA feature. + * + * NDK r10: Support for 64-bit CPUs (Intel, ARM & MIPS). * * NDK r8d: Add android_setCpu(). * @@ -56,16 +60,17 @@ * * NDK r4: Initial release */ -#include -#ifdef __arm__ -#include -#endif -#include + #include "cpu-features.h" + +#include +#include +#include +#include #include #include -#include -#include +#include +#include static pthread_once_t g_once; static int g_inited; @@ -73,16 +78,12 @@ static AndroidCpuFamily g_cpuFamily; static uint64_t g_cpuFeatures; static int g_cpuCount; -static const int android_cpufeatures_debug = 0; - #ifdef __arm__ -# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM -#elif defined __i386__ -# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86 -#else -# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN +static uint32_t g_cpuIdArm; #endif +static const int android_cpufeatures_debug = 0; + #define D(...) \ do { \ if (android_cpufeatures_debug) { \ @@ -109,6 +110,25 @@ static __inline__ void x86_cpuid(int func, int values[4]) values[2] = c; values[3] = d; } +#elif defined(__x86_64__) +static __inline__ void x86_cpuid(int func, int values[4]) +{ + int64_t a, b, c, d; + /* We need to preserve ebx since we're compiling PIC code */ + /* this means we can't use "=b" for the second output register */ + __asm__ __volatile__ ( \ + "push %%rbx\n" + "cpuid\n" \ + "mov %%rbx, %1\n" + "pop %%rbx\n" + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "a" (func) \ + ); + values[0] = a; + values[1] = b; + values[2] = c; + values[3] = d; +} #endif /* Get the size of a file by reading it until the end. This is needed @@ -118,7 +138,8 @@ static __inline__ void x86_cpuid(int func, int values[4]) static int get_file_size(const char* pathname) { - int fd, ret, result = 0; + + int fd, result = 0; char buffer[256]; fd = open(pathname, O_RDONLY); @@ -178,6 +199,7 @@ read_file(const char* pathname, char* buffer, size_t buffsize) return count; } +#ifdef __arm__ /* Extract the content of a the first occurence of a given field in * the content of /proc/cpuinfo and return it as a heap-allocated * string that must be freed by the caller. @@ -190,12 +212,11 @@ extract_cpuinfo_field(const char* buffer, int buflen, const char* field) int fieldlen = strlen(field); const char* bufend = buffer + buflen; char* result = NULL; - int len, ignore; + int len; const char *p, *q; /* Look for first field occurence, and ensures it starts the line. */ p = buffer; - bufend = buffer + buflen; for (;;) { p = memmem(p, bufend-p, field, fieldlen); if (p == NULL) @@ -232,10 +253,6 @@ EXIT: return result; } -/* Like strlen(), but for constant string literals */ -#define STRLEN_CONST(x) ((sizeof(x)-1) - - /* Checks that a space-separated list of items contains one given 'item'. * Returns 1 if found, 0 otherwise. */ @@ -268,8 +285,9 @@ has_list_item(const char* list, const char* item) } return 0; } +#endif /* __arm__ */ -/* Parse an decimal integer starting from 'input', but not going further +/* Parse a number starting from 'input', but not going further * than 'limit'. Return the value into '*result'. * * NOTE: Does not skip over leading spaces, or deal with sign characters. @@ -280,15 +298,23 @@ has_list_item(const char* list, const char* item) * be <= 'limit'). */ static const char* -parse_decimal(const char* input, const char* limit, int* result) +parse_number(const char* input, const char* limit, int base, int* result) { const char* p = input; int val = 0; while (p < limit) { int d = (*p - '0'); - if ((unsigned)d >= 10U) - break; - val = val*10 + d; + if ((unsigned)d >= 10U) { + d = (*p - 'a'); + if ((unsigned)d >= 6U) + d = (*p - 'A'); + if ((unsigned)d >= 6U) + break; + d += 10; + } + if (d >= base) + break; + val = val*base + d; p++; } if (p == input) @@ -298,6 +324,20 @@ parse_decimal(const char* input, const char* limit, int* result) return p; } +static const char* +parse_decimal(const char* input, const char* limit, int* result) +{ + return parse_number(input, limit, 10, result); +} + +#ifdef __arm__ +static const char* +parse_hexadecimal(const char* input, const char* limit, int* result) +{ + return parse_number(input, limit, 16, result); +} +#endif /* __arm__ */ + /* This small data type is used to represent a CPU list / mask, as read * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt * @@ -408,6 +448,18 @@ cpulist_read_from(CpuList* list, const char* filename) cpulist_parse(list, file, filelen); } +#if defined(__aarch64__) +// see kernel header +#define HWCAP_FP (1 << 0) +#define HWCAP_ASIMD (1 << 1) +#define HWCAP_AES (1 << 3) +#define HWCAP_PMULL (1 << 4) +#define HWCAP_SHA1 (1 << 5) +#define HWCAP_SHA2 (1 << 6) +#define HWCAP_CRC32 (1 << 7) +#endif + +#if defined(__arm__) // See kernel header. #define HWCAP_VFP (1 << 6) @@ -419,27 +471,84 @@ cpulist_read_from(CpuList* list, const char* filename) #define HWCAP_IDIVA (1 << 17) #define HWCAP_IDIVT (1 << 18) +// see kernel header +#define HWCAP2_AES (1 << 0) +#define HWCAP2_PMULL (1 << 1) +#define HWCAP2_SHA1 (1 << 2) +#define HWCAP2_SHA2 (1 << 3) +#define HWCAP2_CRC32 (1 << 4) + +// This is the list of 32-bit ARMv7 optional features that are _always_ +// supported by ARMv8 CPUs, as mandated by the ARM Architecture Reference +// Manual. +#define HWCAP_SET_FOR_ARMV8 \ + ( HWCAP_VFP | \ + HWCAP_NEON | \ + HWCAP_VFPv3 | \ + HWCAP_VFPv4 | \ + HWCAP_IDIVA | \ + HWCAP_IDIVT ) +#endif + +#if defined(__mips__) +// see kernel header +#define HWCAP_MIPS_R6 (1 << 0) +#define HWCAP_MIPS_MSA (1 << 1) +#endif + +#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) + #define AT_HWCAP 16 +#define AT_HWCAP2 26 + +// Probe the system's C library for a 'getauxval' function and call it if +// it exits, or return 0 for failure. This function is available since API +// level 20. +// +// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the +// edge case where some NDK developers use headers for a platform that is +// newer than the one really targetted by their application. +// This is typically done to use newer native APIs only when running on more +// recent Android versions, and requires careful symbol management. +// +// Note that getauxval() can't really be re-implemented here, because +// its implementation does not parse /proc/self/auxv. Instead it depends +// on values that are passed by the kernel at process-init time to the +// C runtime initialization layer. +static uint32_t +get_elf_hwcap_from_getauxval(int hwcap_type) { + typedef unsigned long getauxval_func_t(unsigned long); + + dlerror(); + void* libc_handle = dlopen("libc.so", RTLD_NOW); + if (!libc_handle) { + D("Could not dlopen() C library: %s\n", dlerror()); + return 0; + } + + uint32_t ret = 0; + getauxval_func_t* func = (getauxval_func_t*) + dlsym(libc_handle, "getauxval"); + if (!func) { + D("Could not find getauxval() in C library\n"); + } else { + // Note: getauxval() returns 0 on failure. Doesn't touch errno. + ret = (uint32_t)(*func)(hwcap_type); + } + dlclose(libc_handle); + return ret; +} +#endif #if defined(__arm__) -/* Compute the ELF HWCAP flags. - */ +// Parse /proc/self/auxv to extract the ELF HW capabilities bitmap for the +// current CPU. Note that this file is not accessible from regular +// application processes on some Android platform releases. +// On success, return new ELF hwcaps, or 0 on failure. static uint32_t -get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) -{ - /* IMPORTANT: - * Accessing /proc/self/auxv doesn't work anymore on all - * platform versions. More specifically, when running inside - * a regular application process, most of /proc/self/ will be - * non-readable, including /proc/self/auxv. This doesn't - * happen however if the application is debuggable, or when - * running under the "shell" UID, which is why this was not - * detected appropriately. - */ -#if 0 - uint32_t result = 0; +get_elf_hwcap_from_proc_self_auxv(void) { const char filepath[] = "/proc/self/auxv"; - int fd = open(filepath, O_RDONLY); + int fd = TEMP_FAILURE_RETRY(open(filepath, O_RDONLY)); if (fd < 0) { D("Could not open %s: %s\n", filepath, strerror(errno)); return 0; @@ -447,11 +556,10 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) struct { uint32_t tag; uint32_t value; } entry; + uint32_t result = 0; for (;;) { - int ret = read(fd, (char*)&entry, sizeof entry); + int ret = TEMP_FAILURE_RETRY(read(fd, (char*)&entry, sizeof entry)); if (ret < 0) { - if (errno == EINTR) - continue; D("Error while reading %s: %s\n", filepath, strerror(errno)); break; } @@ -465,12 +573,33 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) } close(fd); return result; -#else - // Recreate ELF hwcaps by parsing /proc/cpuinfo Features tag. +} + +/* Compute the ELF HWCAP flags from the content of /proc/cpuinfo. + * This works by parsing the 'Features' line, which lists which optional + * features the device's CPU supports, on top of its reference + * architecture. + */ +static uint32_t +get_elf_hwcap_from_proc_cpuinfo(const char* cpuinfo, int cpuinfo_len) { uint32_t hwcaps = 0; + long architecture = 0; + char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); + if (cpuArch) { + architecture = strtol(cpuArch, NULL, 10); + free(cpuArch); + + if (architecture >= 8L) { + // This is a 32-bit ARM binary running on a 64-bit ARM64 kernel. + // The 'Features' line only lists the optional features that the + // device's CPU supports, compared to its reference architecture + // which are of no use for this process. + D("Faking 32-bit ARM HWCaps on ARMv%ld CPU\n", architecture); + return HWCAP_SET_FOR_ARMV8; + } + } char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features"); - if (cpuFeatures != NULL) { D("Found cpuFeatures = '%s'\n", cpuFeatures); @@ -496,7 +625,6 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) free(cpuFeatures); } return hwcaps; -#endif } #endif /* __arm__ */ @@ -526,12 +654,19 @@ get_cpu_count(void) static void android_cpuInitFamily(void) { -#if defined(__ARM_ARCH__) +#if defined(__arm__) g_cpuFamily = ANDROID_CPU_FAMILY_ARM; #elif defined(__i386__) g_cpuFamily = ANDROID_CPU_FAMILY_X86; -#elif defined(_MIPS_ARCH) +#elif defined(__mips64) +/* Needs to be before __mips__ since the compiler defines both */ + g_cpuFamily = ANDROID_CPU_FAMILY_MIPS64; +#elif defined(__mips__) g_cpuFamily = ANDROID_CPU_FAMILY_MIPS; +#elif defined(__aarch64__) + g_cpuFamily = ANDROID_CPU_FAMILY_ARM64; +#elif defined(__x86_64__) + g_cpuFamily = ANDROID_CPU_FAMILY_X86_64; #else g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN; #endif @@ -576,11 +711,8 @@ android_cpuInit(void) D("found cpuCount = %d\n", g_cpuCount); -#ifdef __ARM_ARCH__ +#ifdef __arm__ { - char* features = NULL; - char* architecture = NULL; - /* Extract architecture from the "CPU Architecture" field. * The list is well-known, unlike the the output of * the 'Processor' field which can vary greatly. @@ -601,10 +733,7 @@ android_cpuInit(void) /* read the initial decimal number, ignore the rest */ archNumber = strtol(cpuArch, &end, 10); - /* Here we assume that ARMv8 will be upwards compatible with v7 - * in the future. Unfortunately, there is no 'Features' field to - * indicate that Thumb-2 is supported. - */ + /* Note that ARMv8 is upwards compatible with ARMv7. */ if (end > cpuArch && archNumber >= 7) { hasARMv7 = 1; } @@ -645,7 +774,19 @@ android_cpuInit(void) } /* Extract the list of CPU features from ELF hwcaps */ - uint32_t hwcaps = get_elf_hwcap(cpuinfo, cpuinfo_len); + uint32_t hwcaps = 0; + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); + if (!hwcaps) { + D("Parsing /proc/self/auxv to extract ELF hwcaps!\n"); + hwcaps = get_elf_hwcap_from_proc_self_auxv(); + } + if (!hwcaps) { + // Parsing /proc/self/auxv will fail from regular application + // processes on some Android platform versions, when this happens + // parse proc/cpuinfo instead. + D("Parsing /proc/cpuinfo to extract ELF hwcaps!\n"); + hwcaps = get_elf_hwcap_from_proc_cpuinfo(cpuinfo, cpuinfo_len); + } if (hwcaps != 0) { int has_vfp = (hwcaps & HWCAP_VFP); @@ -697,22 +838,163 @@ android_cpuInit(void) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 | ANDROID_CPU_ARM_FEATURE_ARMv7; - // Note that some buggy kernels do not report these even when - // the CPU actually support the division instructions. However, - // assume that if 'vfpv4' is detected, then the CPU supports - // sdiv/udiv properly. - if (has_idiva || has_vfpv4) + if (has_idiva) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; - if (has_idivt || has_vfpv4) + if (has_idivt) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2; if (has_iwmmxt) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt; } - } -#endif /* __ARM_ARCH__ */ -#ifdef __i386__ + /* Extract the list of CPU features from ELF hwcaps2 */ + uint32_t hwcaps2 = 0; + hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2); + if (hwcaps2 != 0) { + int has_aes = (hwcaps2 & HWCAP2_AES); + int has_pmull = (hwcaps2 & HWCAP2_PMULL); + int has_sha1 = (hwcaps2 & HWCAP2_SHA1); + int has_sha2 = (hwcaps2 & HWCAP2_SHA2); + int has_crc32 = (hwcaps2 & HWCAP2_CRC32); + + if (has_aes) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES; + if (has_pmull) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL; + if (has_sha1) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1; + if (has_sha2) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2; + if (has_crc32) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32; + } + /* Extract the cpuid value from various fields */ + // The CPUID value is broken up in several entries in /proc/cpuinfo. + // This table is used to rebuild it from the entries. + static const struct CpuIdEntry { + const char* field; + char format; + char bit_lshift; + char bit_length; + } cpu_id_entries[] = { + { "CPU implementer", 'x', 24, 8 }, + { "CPU variant", 'x', 20, 4 }, + { "CPU part", 'x', 4, 12 }, + { "CPU revision", 'd', 0, 4 }, + }; + size_t i; + D("Parsing /proc/cpuinfo to recover CPUID\n"); + for (i = 0; + i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]); + ++i) { + const struct CpuIdEntry* entry = &cpu_id_entries[i]; + char* value = extract_cpuinfo_field(cpuinfo, + cpuinfo_len, + entry->field); + if (value == NULL) + continue; + + D("field=%s value='%s'\n", entry->field, value); + char* value_end = value + strlen(value); + int val = 0; + const char* start = value; + const char* p; + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + start += 2; + p = parse_hexadecimal(start, value_end, &val); + } else if (entry->format == 'x') + p = parse_hexadecimal(value, value_end, &val); + else + p = parse_decimal(value, value_end, &val); + + if (p > (const char*)start) { + val &= ((1 << entry->bit_length)-1); + val <<= entry->bit_lshift; + g_cpuIdArm |= (uint32_t) val; + } + + free(value); + } + + // Handle kernel configuration bugs that prevent the correct + // reporting of CPU features. + static const struct CpuFix { + uint32_t cpuid; + uint64_t or_flags; + } cpu_fixes[] = { + /* The Nexus 4 (Qualcomm Krait) kernel configuration + * forgets to report IDIV support. */ + { 0x510006f2, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | + ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 }, + { 0x510006f3, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | + ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 }, + }; + size_t n; + for (n = 0; n < sizeof(cpu_fixes)/sizeof(cpu_fixes[0]); ++n) { + const struct CpuFix* entry = &cpu_fixes[n]; + + if (g_cpuIdArm == entry->cpuid) + g_cpuFeatures |= entry->or_flags; + } + + // Special case: The emulator-specific Android 4.2 kernel fails + // to report support for the 32-bit ARM IDIV instruction. + // Technically, this is a feature of the virtual CPU implemented + // by the emulator. Note that it could also support Thumb IDIV + // in the future, and this will have to be slightly updated. + char* hardware = extract_cpuinfo_field(cpuinfo, + cpuinfo_len, + "Hardware"); + if (hardware) { + if (!strcmp(hardware, "Goldfish") && + g_cpuIdArm == 0x4100c080 && + (g_cpuFamily & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0) { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; + } + free(hardware); + } + } +#endif /* __arm__ */ +#ifdef __aarch64__ + { + /* Extract the list of CPU features from ELF hwcaps */ + uint32_t hwcaps = 0; + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); + if (hwcaps != 0) { + int has_fp = (hwcaps & HWCAP_FP); + int has_asimd = (hwcaps & HWCAP_ASIMD); + int has_aes = (hwcaps & HWCAP_AES); + int has_pmull = (hwcaps & HWCAP_PMULL); + int has_sha1 = (hwcaps & HWCAP_SHA1); + int has_sha2 = (hwcaps & HWCAP_SHA2); + int has_crc32 = (hwcaps & HWCAP_CRC32); + + if(has_fp == 0) { + D("ERROR: Floating-point unit missing, but is required by Android on AArch64 CPUs\n"); + } + if(has_asimd == 0) { + D("ERROR: ASIMD unit missing, but is required by Android on AArch64 CPUs\n"); + } + + if (has_fp) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP; + if (has_asimd) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD; + if (has_aes) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES; + if (has_pmull) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL; + if (has_sha1) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1; + if (has_sha2) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2; + if (has_crc32) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32; + } + } +#endif /* __aarch64__ */ + +#if defined(__i386__) || defined(__x86_64__) int regs[4]; /* According to http://en.wikipedia.org/wiki/CPUID */ @@ -732,10 +1014,50 @@ android_cpuInit(void) if ((regs[2] & (1 << 23)) != 0) { g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; } + if ((regs[2] & (1 << 19)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1; + } + if ((regs[2] & (1 << 20)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2; + } if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) { g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; } + if ((regs[2] & (1 << 25)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AES_NI; + } + if ((regs[2] & (1 << 28)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX; + } + if ((regs[2] & (1 << 30)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_RDRAND; + } + + x86_cpuid(7, regs); + if ((regs[1] & (1 << 5)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX2; + } + if ((regs[1] & (1 << 29)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SHA_NI; + } + + #endif +#if defined( __mips__) + { /* MIPS and MIPS64 */ + /* Extract the list of CPU features from ELF hwcaps */ + uint32_t hwcaps = 0; + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); + if (hwcaps != 0) { + int has_r6 = (hwcaps & HWCAP_MIPS_R6); + int has_msa = (hwcaps & HWCAP_MIPS_MSA); + if (has_r6) + g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6; + if (has_msa) + g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA; + } + } +#endif /* __mips__ */ free(cpuinfo); } @@ -785,6 +1107,25 @@ android_setCpu(int cpu_count, uint64_t cpu_features) return 1; } +#ifdef __arm__ +uint32_t +android_getCpuIdArm(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuIdArm; +} + +int +android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id) +{ + if (!android_setCpu(cpu_count, cpu_features)) + return 0; + + g_cpuIdArm = cpu_id; + return 1; +} +#endif /* __arm__ */ + /* * Technical note: Making sense of ARM's FPU architecture versions. * diff --git a/3rdparty/libwebp/cpu-features/cpu-features.h b/3rdparty/cpufeatures/cpu-features.h similarity index 59% rename from 3rdparty/libwebp/cpu-features/cpu-features.h rename to 3rdparty/cpufeatures/cpu-features.h index f8553e88d5..1e9724197a 100644 --- a/3rdparty/libwebp/cpu-features/cpu-features.h +++ b/3rdparty/cpufeatures/cpu-features.h @@ -33,21 +33,40 @@ __BEGIN_DECLS +/* A list of valid values returned by android_getCpuFamily(). + * They describe the CPU Architecture of the current process. + */ typedef enum { ANDROID_CPU_FAMILY_UNKNOWN = 0, ANDROID_CPU_FAMILY_ARM, ANDROID_CPU_FAMILY_X86, ANDROID_CPU_FAMILY_MIPS, + ANDROID_CPU_FAMILY_ARM64, + ANDROID_CPU_FAMILY_X86_64, + ANDROID_CPU_FAMILY_MIPS64, ANDROID_CPU_FAMILY_MAX /* do not remove */ } AndroidCpuFamily; -/* Return family of the device's CPU */ -extern AndroidCpuFamily android_getCpuFamily(void); +/* Return the CPU family of the current process. + * + * Note that this matches the bitness of the current process. I.e. when + * running a 32-bit binary on a 64-bit capable CPU, this will return the + * 32-bit CPU family value. + */ +extern AndroidCpuFamily android_getCpuFamily(void); -/* The list of feature flags for ARM CPUs that can be recognized by the - * library. Value details are: +/* Return a bitmap describing a set of optional CPU features that are + * supported by the current device's CPU. The exact bit-flags returned + * depend on the value returned by android_getCpuFamily(). See the + * documentation for the ANDROID_CPU_*_FEATURE_* flags below for details. + */ +extern uint64_t android_getCpuFeatures(void); + +/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be + * recognized by the library (see note below for 64-bit ARM). Value details + * are: * * VFPv2: * CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs @@ -103,6 +122,27 @@ extern AndroidCpuFamily android_getCpuFamily(void); * ARM CPU. This is only available on a few XScale-based CPU designs * sold by Marvell. Pretty rare in practice. * + * AES: + * CPU supports AES instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * CRC32: + * CPU supports CRC32 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * SHA2: + * CPU supports SHA2 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * SHA1: + * CPU supports SHA1 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * PMULL: + * CPU supports 64-bit PMULL and PMULL2 instructions. These + * instructions are only available for 32-bit applications + * running on ARMv8 CPU. + * * If you want to tell the compiler to generate code that targets one of * the feature set above, you should probably use one of the following * flags (for more details, see technical note at the end of this file): @@ -150,6 +190,13 @@ extern AndroidCpuFamily android_getCpuFamily(void); * * -mcpu=iwmmxt * Allows the use of iWMMXt instrinsics with GCC. + * + * IMPORTANT NOTE: These flags should only be tested when + * android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a + * 32-bit process. + * + * When running a 64-bit ARM process on an ARMv8 CPU, + * android_getCpuFeatures() will return a different set of bitflags */ enum { ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0), @@ -164,18 +211,84 @@ enum { ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9), ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10), ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11), + ANDROID_CPU_ARM_FEATURE_AES = (1 << 12), + ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13), + ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14), + ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15), + ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16), }; +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details + * are: + * + * FP: + * CPU has Floating-point unit. + * + * ASIMD: + * CPU has Advanced SIMD unit. + * + * AES: + * CPU supports AES instructions. + * + * CRC32: + * CPU supports CRC32 instructions. + * + * SHA2: + * CPU supports SHA2 instructions. + * + * SHA1: + * CPU supports SHA1 instructions. + * + * PMULL: + * CPU supports 64-bit PMULL and PMULL2 instructions. + */ +enum { + ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0), + ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1), + ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2), + ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3), + ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4), + ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5), + ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6), +}; + +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86 or + * ANDROID_CPU_FAMILY_X86_64. + */ enum { ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0), ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1), ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2), + ANDROID_CPU_X86_FEATURE_SSE4_1 = (1 << 3), + ANDROID_CPU_X86_FEATURE_SSE4_2 = (1 << 4), + ANDROID_CPU_X86_FEATURE_AES_NI = (1 << 5), + ANDROID_CPU_X86_FEATURE_AVX = (1 << 6), + ANDROID_CPU_X86_FEATURE_RDRAND = (1 << 7), + ANDROID_CPU_X86_FEATURE_AVX2 = (1 << 8), + ANDROID_CPU_X86_FEATURE_SHA_NI = (1 << 9), +}; + +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_MIPS + * or ANDROID_CPU_FAMILY_MIPS64. Values are: + * + * R6: + * CPU executes MIPS Release 6 instructions natively, and + * supports obsoleted R1..R5 instructions only via kernel traps. + * + * MSA: + * CPU supports Mips SIMD Architecture instructions. + */ +enum { + ANDROID_CPU_MIPS_FEATURE_R6 = (1 << 0), + ANDROID_CPU_MIPS_FEATURE_MSA = (1 << 1), }; -extern uint64_t android_getCpuFeatures(void); /* Return the number of CPU cores detected on this device. */ -extern int android_getCpuCount(void); +extern int android_getCpuCount(void); /* The following is used to force the CPU count and features * mask in sandboxed processes. Under 4.1 and higher, these processes @@ -190,6 +303,21 @@ extern int android_getCpuCount(void); extern int android_setCpu(int cpu_count, uint64_t cpu_features); +#ifdef __arm__ +/* Retrieve the ARM 32-bit CPUID value from the kernel. + * Note that this cannot work on sandboxed processes under 4.1 and + * higher, unless you called android_setCpuArm() before. + */ +extern uint32_t android_getCpuIdArm(void); + +/* An ARM-specific variant of android_setCpu() that also allows you + * to set the ARM CPUID field. + */ +extern int android_setCpuArm(int cpu_count, + uint64_t cpu_features, + uint32_t cpu_id); +#endif + __END_DECLS #endif /* CPU_FEATURES_H */ diff --git a/3rdparty/libwebp/CMakeLists.txt b/3rdparty/libwebp/CMakeLists.txt index 004697e495..e72932f3e0 100644 --- a/3rdparty/libwebp/CMakeLists.txt +++ b/3rdparty/libwebp/CMakeLists.txt @@ -5,7 +5,7 @@ project(${WEBP_LIBRARY}) ocv_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/cpu-features") +ocv_include_directories("${CPUFEATURES_INCLUDE_DIR}") file(GLOB lib_srcs dec/*.c demux/*.c dsp/*.c enc/*.c mux/*.c utils/*.c webp/*.c) file(GLOB lib_hdrs dec/*.h demux/*.h dsp/*.h enc/*.h mux/*.h utils/*.h webp/*.h) @@ -19,13 +19,7 @@ if(ANDROID AND ARMEABI_V7A AND NOT NEON) endforeach() endif() -file(GLOB cpuf_s cpu-features/*.c) -file(GLOB cpuf_h cpu-features/*.h) -if(ANDROID) - set(lib_srcs ${lib_srcs} ${cpuf_s}) - set(lib_hdrs ${lib_hdrs} ${cpuf_h}) -endif() # ---------------------------------------------------------------------------------- # Define the library target: @@ -34,6 +28,7 @@ endif() add_definitions(-DWEBP_USE_THREAD) add_library(${WEBP_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs}) +target_link_libraries(${WEBP_LIBRARY} ${CPUFEATURES_LIBRARIES}) if(UNIX) if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3223182746..5d1c925cae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,6 +573,10 @@ endif() # Detect 3rd-party libraries # ---------------------------------------------------------------------------- +if(ANDROID) + add_subdirectory(3rdparty/cpufeatures) +endif() + include(cmake/OpenCVFindLibsGrfmt.cmake) include(cmake/OpenCVFindLibsGUI.cmake) include(cmake/OpenCVFindLibsVideo.cmake) diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 3d2cc7f594..ca4a753dbd 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -32,10 +32,10 @@ source_group("Src" FILES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string ocv_glob_module_sources(SOURCES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc" HEADERS ${lib_cuda_hdrs} ${lib_cuda_hdrs_detail}) -ocv_module_include_directories(${the_module} ${ZLIB_INCLUDE_DIRS} ${OPENCL_INCLUDE_DIRS}) +ocv_module_include_directories(${the_module} ${ZLIB_INCLUDE_DIRS} ${OPENCL_INCLUDE_DIRS} ${CPUFEATURES_INCLUDE_DIR}) ocv_create_module(${extra_libs}) -ocv_target_link_libraries(${the_module} ${ZLIB_LIBRARIES} "${OPENCL_LIBRARIES}" "${VA_LIBRARIES}" "${LAPACK_LIBRARIES}") +ocv_target_link_libraries(${the_module} ${ZLIB_LIBRARIES} "${OPENCL_LIBRARIES}" "${VA_LIBRARIES}" "${LAPACK_LIBRARIES}" "${CPUFEATURES_LIBRARIES}") ocv_add_accuracy_tests() ocv_add_perf_tests() diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 138fc476fa..6fa20a93cb 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -73,6 +73,10 @@ Mutex* __initialization_mutex_initializer = &getInitializationMutex(); #endif #endif +#if defined ANDROID +# include +#endif + #if defined WIN32 || defined _WIN32 || defined WINCE #ifndef _WIN32_WINNT // This is needed for the declaration of TryEnterCriticalSection in winbase.h with Visual Studio 2005 (and older?) #define _WIN32_WINNT 0x0400 // http://msdn.microsoft.com/en-us/library/ms686857(VS.85).aspx @@ -467,6 +471,11 @@ struct HWFeatures close(cpufile); } + #ifdef ANDROID + uint64_t features = android_getCpuFeatures(); + have[CV_CPU_NEON] = (features & ANDROID_CPU_ARM_FEATURE_NEON) != 0; + have[CV_CPU_FP16] = (features & ANDROID_CPU_ARM_FEATURE_VFP_FP16) != 0; + #endif #endif #elif (defined __clang__ || defined __APPLE__) #if (defined __ARM_NEON__ || (defined __ARM_NEON && defined __aarch64__))