58cf1157c307c604f59a16f43a923c87d309a809
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / core / SkUtilsArm.cpp
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