michael@0: michael@0: /* michael@0: * Copyright 2012 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkUtilsArm.h" michael@0: michael@0: #if SK_ARM_NEON_IS_DYNAMIC michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: // Set USE_ANDROID_NDK_CPU_FEATURES to use the Android NDK's michael@0: // cpu-features helper library to detect NEON at runtime. See michael@0: // http://crbug.com/164154 to see why this is needed in Chromium michael@0: // for Android. michael@0: #if !defined(USE_ANDROID_NDK_CPU_FEATURES) michael@0: # if defined(SK_BUILD_FOR_ANDROID) michael@0: # define USE_ANDROID_NDK_CPU_FEATURES 1 michael@0: # else michael@0: # define USE_ANDROID_NDK_CPU_FEATURES 0 michael@0: # endif michael@0: #endif michael@0: michael@0: #if USE_ANDROID_NDK_CPU_FEATURES michael@0: # include michael@0: #endif michael@0: michael@0: // Set NEON_DEBUG to 1 to allow debugging of the CPU features probing. michael@0: // For now, we always set it for SK_DEBUG builds. michael@0: #ifdef SK_DEBUG michael@0: # define NEON_DEBUG 1 michael@0: #else michael@0: # define NEON_DEBUG 0 michael@0: #endif michael@0: michael@0: #if NEON_DEBUG michael@0: # ifdef SK_BUILD_FOR_ANDROID michael@0: // used to declare PROP_VALUE_MAX and __system_property_get() michael@0: # include michael@0: # endif michael@0: #endif michael@0: michael@0: // A function used to determine at runtime if the target CPU supports michael@0: // the ARM NEON instruction set. This implementation is Linux-specific. michael@0: static bool sk_cpu_arm_check_neon(void) { michael@0: bool result = false; michael@0: michael@0: #if NEON_DEBUG michael@0: // Allow forcing the mode through the environment during debugging. michael@0: # ifdef SK_BUILD_FOR_ANDROID michael@0: // On Android, we use a system property michael@0: # define PROP_NAME "debug.skia.arm_neon_mode" michael@0: char prop[PROP_VALUE_MAX]; michael@0: if (__system_property_get(PROP_NAME, prop) > 0) { michael@0: # else michael@0: # define PROP_NAME "SKIA_ARM_NEON_MODE" michael@0: // On ARM Linux, we use an environment variable michael@0: const char* prop = getenv(PROP_NAME); michael@0: if (prop != NULL) { michael@0: # endif michael@0: SkDebugf("%s: %s", PROP_NAME, prop); michael@0: if (!strcmp(prop, "1")) { michael@0: SkDebugf("Forcing ARM Neon mode to full!\n"); michael@0: return true; michael@0: } michael@0: if (!strcmp(prop, "0")) { michael@0: SkDebugf("Disabling ARM NEON mode\n"); michael@0: return false; michael@0: } michael@0: } michael@0: SkDebugf("Running dynamic CPU feature detection\n"); michael@0: #endif michael@0: michael@0: #if USE_ANDROID_NDK_CPU_FEATURES michael@0: michael@0: result = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; michael@0: michael@0: #else // USE_ANDROID_NDK_CPU_FEATURES michael@0: michael@0: // There is no user-accessible CPUID instruction on ARM that we can use. michael@0: // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature. michael@0: // For example, here's a typical output (Nexus S running ICS 4.0.3): michael@0: /* michael@0: Processor : ARMv7 Processor rev 2 (v7l) michael@0: BogoMIPS : 994.65 michael@0: Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 michael@0: CPU implementer : 0x41 michael@0: CPU architecture: 7 michael@0: CPU variant : 0x2 michael@0: CPU part : 0xc08 michael@0: CPU revision : 2 michael@0: michael@0: Hardware : herring michael@0: Revision : 000b michael@0: Serial : 3833c77d6dc000ec michael@0: */ michael@0: char buffer[4096]; michael@0: michael@0: // If we fail any of the following, assume we don't have NEON instructions michael@0: // This allows us to return immediately in case of error. michael@0: result = false; michael@0: michael@0: do { michael@0: // open /proc/cpuinfo michael@0: int fd = TEMP_FAILURE_RETRY(open("/proc/cpuinfo", O_RDONLY)); michael@0: if (fd < 0) { michael@0: SkDebugf("Could not open /proc/cpuinfo: %s\n", strerror(errno)); michael@0: break; michael@0: } michael@0: michael@0: // Read the file. To simplify our search, we're going to place two michael@0: // sentinel '\n' characters: one at the start of the buffer, and one at michael@0: // the end. This means we reserve the first and last buffer bytes. michael@0: buffer[0] = '\n'; michael@0: int size = TEMP_FAILURE_RETRY(read(fd, buffer+1, sizeof(buffer)-2)); michael@0: close(fd); michael@0: michael@0: if (size < 0) { // should not happen michael@0: SkDebugf("Could not read /proc/cpuinfo: %s\n", strerror(errno)); michael@0: break; michael@0: } michael@0: michael@0: SkDebugf("START /proc/cpuinfo:\n%.*s\nEND /proc/cpuinfo\n", michael@0: size, buffer+1); michael@0: michael@0: // Compute buffer limit, and place final sentinel michael@0: char* buffer_end = buffer + 1 + size; michael@0: buffer_end[0] = '\n'; michael@0: michael@0: // Now, find a line that starts with "Features", i.e. look for michael@0: // '\nFeatures ' in our buffer. michael@0: const char features[] = "\nFeatures\t"; michael@0: const size_t features_len = sizeof(features)-1; michael@0: michael@0: char* line = (char*) memmem(buffer, buffer_end - buffer, michael@0: features, features_len); michael@0: if (line == NULL) { // Weird, no Features line, bad kernel? michael@0: SkDebugf("Could not find a line starting with 'Features'" michael@0: "in /proc/cpuinfo ?\n"); michael@0: break; michael@0: } michael@0: michael@0: line += features_len; // Skip the "\nFeatures\t" prefix michael@0: michael@0: // Find the end of the current line michael@0: char* line_end = (char*) memchr(line, '\n', buffer_end - line); michael@0: if (line_end == NULL) michael@0: line_end = buffer_end; michael@0: michael@0: // Now find an instance of 'neon' in the flags list. We want to michael@0: // ensure it's only 'neon' and not something fancy like 'noneon' michael@0: // so check that it follows a space. michael@0: const char neon[] = " neon"; michael@0: const size_t neon_len = sizeof(neon)-1; michael@0: const char* flag = (const char*) memmem(line, line_end - line, michael@0: neon, neon_len); michael@0: if (flag == NULL) michael@0: break; michael@0: michael@0: // Ensure it is followed by a space or a newline. michael@0: if (flag[neon_len] != ' ' && flag[neon_len] != '\n') michael@0: break; michael@0: michael@0: // Fine, we support Arm NEON ! michael@0: result = true; michael@0: michael@0: } while (0); michael@0: michael@0: #endif // USE_ANDROID_NDK_CPU_FEATURES michael@0: michael@0: if (result) { michael@0: SkDebugf("Device supports ARM NEON instructions!\n"); michael@0: } else { michael@0: SkDebugf("Device does NOT support ARM NEON instructions!\n"); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static pthread_once_t sOnce; michael@0: static bool sHasArmNeon; michael@0: michael@0: // called through pthread_once() michael@0: void sk_cpu_arm_probe_features(void) { michael@0: sHasArmNeon = sk_cpu_arm_check_neon(); michael@0: } michael@0: michael@0: bool sk_cpu_arm_has_neon(void) { michael@0: pthread_once(&sOnce, sk_cpu_arm_probe_features); michael@0: return sHasArmNeon; michael@0: } michael@0: michael@0: #endif // SK_ARM_NEON_IS_DYNAMIC