Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2012 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "SkUtilsArm.h"
     10 
     11 #if SK_ARM_NEON_IS_DYNAMIC
     12 
     13 #include <unistd.h>
     14 #include <fcntl.h>
     15 #include <errno.h>
     16 #include <string.h>
     17 #include <pthread.h>
     18 
     19 // Set USE_ANDROID_NDK_CPU_FEATURES to use the Android NDK's
     20 // cpu-features helper library to detect NEON at runtime. See
     21 // http://crbug.com/164154 to see why this is needed in Chromium
     22 // for Android.
     23 #if !defined(USE_ANDROID_NDK_CPU_FEATURES)
     24 #  if defined(SK_BUILD_FOR_ANDROID)
     25 #    define USE_ANDROID_NDK_CPU_FEATURES 1
     26 #  else
     27 #    define USE_ANDROID_NDK_CPU_FEATURES 0
     28 #  endif
     29 #endif
     30 
     31 #if USE_ANDROID_NDK_CPU_FEATURES
     32 #  include <cpu-features.h>
     33 #endif
     34 
     35 // Set NEON_DEBUG to 1 to allow debugging of the CPU features probing.
     36 // For now, we always set it for SK_DEBUG builds.
     37 #ifdef SK_DEBUG
     38 #  define NEON_DEBUG  1
     39 #else
     40 #  define NEON_DEBUG 0
     41 #endif
     42 
     43 #if NEON_DEBUG
     44 #  ifdef SK_BUILD_FOR_ANDROID
     45      // used to declare PROP_VALUE_MAX and __system_property_get()
     46 #    include <sys/system_properties.h>
     47 #  endif
     48 #endif
     49 
     50 // A function used to determine at runtime if the target CPU supports
     51 // the ARM NEON instruction set. This implementation is Linux-specific.
     52 static bool sk_cpu_arm_check_neon(void) {
     53     bool result = false;
     54 
     55 #if NEON_DEBUG
     56     // Allow forcing the mode through the environment during debugging.
     57 #  ifdef SK_BUILD_FOR_ANDROID
     58     // On Android, we use a system property
     59 #   define PROP_NAME  "debug.skia.arm_neon_mode"
     60     char prop[PROP_VALUE_MAX];
     61     if (__system_property_get(PROP_NAME, prop) > 0) {
     62 #  else
     63 #   define PROP_NAME   "SKIA_ARM_NEON_MODE"
     64     // On ARM Linux, we use an environment variable
     65     const char* prop = getenv(PROP_NAME);
     66     if (prop != NULL) {
     67 #  endif
     68         SkDebugf("%s: %s", PROP_NAME, prop);
     69         if (!strcmp(prop, "1")) {
     70             SkDebugf("Forcing ARM Neon mode to full!\n");
     71             return true;
     72         }
     73         if (!strcmp(prop, "0")) {
     74             SkDebugf("Disabling ARM NEON mode\n");
     75             return false;
     76         }
     77     }
     78     SkDebugf("Running dynamic CPU feature detection\n");
     79 #endif
     80 
     81 #if USE_ANDROID_NDK_CPU_FEATURES
     82 
     83   result = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
     84 
     85 #else  // USE_ANDROID_NDK_CPU_FEATURES
     86 
     87     // There is no user-accessible CPUID instruction on ARM that we can use.
     88     // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature.
     89     // For example, here's a typical output (Nexus S running ICS 4.0.3):
     90     /*
     91     Processor       : ARMv7 Processor rev 2 (v7l)
     92     BogoMIPS        : 994.65
     93     Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3
     94     CPU implementer : 0x41
     95     CPU architecture: 7
     96     CPU variant     : 0x2
     97     CPU part        : 0xc08
     98     CPU revision    : 2
     99 
    100     Hardware        : herring
    101     Revision        : 000b
    102     Serial          : 3833c77d6dc000ec
    103     */
    104     char   buffer[4096];
    105 
    106     // If we fail any of the following, assume we don't have NEON instructions
    107     // This allows us to return immediately in case of error.
    108     result = false;
    109 
    110     do {
    111         // open /proc/cpuinfo
    112         int fd = TEMP_FAILURE_RETRY(open("/proc/cpuinfo", O_RDONLY));
    113         if (fd < 0) {
    114             SkDebugf("Could not open /proc/cpuinfo: %s\n", strerror(errno));
    115             break;
    116         }
    117 
    118         // Read the file. To simplify our search, we're going to place two
    119         // sentinel '\n' characters: one at the start of the buffer, and one at
    120         // the end. This means we reserve the first and last buffer bytes.
    121         buffer[0] = '\n';
    122         int size = TEMP_FAILURE_RETRY(read(fd, buffer+1, sizeof(buffer)-2));
    123         close(fd);
    124 
    125         if (size < 0) {  // should not happen
    126             SkDebugf("Could not read /proc/cpuinfo: %s\n", strerror(errno));
    127             break;
    128         }
    129 
    130         SkDebugf("START /proc/cpuinfo:\n%.*s\nEND /proc/cpuinfo\n",
    131                  size, buffer+1);
    132 
    133         // Compute buffer limit, and place final sentinel
    134         char* buffer_end = buffer + 1 + size;
    135         buffer_end[0] = '\n';
    136 
    137         // Now, find a line that starts with "Features", i.e. look for
    138         // '\nFeatures ' in our buffer.
    139         const char features[] = "\nFeatures\t";
    140         const size_t features_len = sizeof(features)-1;
    141 
    142         char*  line = (char*) memmem(buffer, buffer_end - buffer,
    143                                      features, features_len);
    144         if (line == NULL) {  // Weird, no Features line, bad kernel?
    145             SkDebugf("Could not find a line starting with 'Features'"
    146               "in /proc/cpuinfo ?\n");
    147             break;
    148         }
    149 
    150         line += features_len;  // Skip the "\nFeatures\t" prefix
    151 
    152         // Find the end of the current line
    153         char* line_end = (char*) memchr(line, '\n', buffer_end - line);
    154         if (line_end == NULL)
    155             line_end = buffer_end;
    156 
    157         // Now find an instance of 'neon' in the flags list. We want to
    158         // ensure it's only 'neon' and not something fancy like 'noneon'
    159         // so check that it follows a space.
    160         const char neon[] = " neon";
    161         const size_t neon_len = sizeof(neon)-1;
    162         const char* flag = (const char*) memmem(line, line_end - line,
    163                                                 neon, neon_len);
    164         if (flag == NULL)
    165             break;
    166 
    167         // Ensure it is followed by a space or a newline.
    168         if (flag[neon_len] != ' ' && flag[neon_len] != '\n')
    169             break;
    170 
    171         // Fine, we support Arm NEON !
    172         result = true;
    173 
    174     } while (0);
    175 
    176 #endif  // USE_ANDROID_NDK_CPU_FEATURES
    177 
    178     if (result) {
    179         SkDebugf("Device supports ARM NEON instructions!\n");
    180     } else {
    181         SkDebugf("Device does NOT support ARM NEON instructions!\n");
    182     }
    183     return result;
    184 }
    185 
    186 static pthread_once_t  sOnce;
    187 static bool            sHasArmNeon;
    188 
    189 // called through pthread_once()
    190 void sk_cpu_arm_probe_features(void) {
    191     sHasArmNeon = sk_cpu_arm_check_neon();
    192 }
    193 
    194 bool sk_cpu_arm_has_neon(void) {
    195     pthread_once(&sOnce, sk_cpu_arm_probe_features);
    196     return sHasArmNeon;
    197 }
    198 
    199 #endif // SK_ARM_NEON_IS_DYNAMIC
    200