aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkUtilsArm.cpp
blob: bf98fed47691e5b4cd8549f092ccfeb80e91d461 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 * Copyright 2012 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkUtilsArm.h"

#if SK_ARM_NEON_IS_DYNAMIC

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>

#if defined(SK_BUILD_FOR_ANDROID)
#  include <cpu-features.h>
#endif

// A function used to determine at runtime if the target CPU supports
// the ARM NEON instruction set. This implementation is Linux-specific.
static bool sk_cpu_arm_check_neon(void) {
    // If we fail any of the following, assume we don't have NEON instructions
    // This allows us to return immediately in case of error.
    bool result = false;

// Use the Android NDK's cpu-features helper library to detect NEON at runtime.
// See http://crbug.com/164154 to see why this is needed in Chromium for Android.
#ifdef SK_BUILD_FOR_ANDROID

  result = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;

#else  // SK_BUILD_FOR_ANDROID

    // There is no user-accessible CPUID instruction on ARM that we can use.
    // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature.
    // For example, here's a typical output (Nexus S running ICS 4.0.3):
    /*
    Processor       : ARMv7 Processor rev 2 (v7l)
    BogoMIPS        : 994.65
    Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3
    CPU implementer : 0x41
    CPU architecture: 7
    CPU variant     : 0x2
    CPU part        : 0xc08
    CPU revision    : 2

    Hardware        : herring
    Revision        : 000b
    Serial          : 3833c77d6dc000ec
    */
    char   buffer[4096];

    do {
        // open /proc/cpuinfo
        int fd = TEMP_FAILURE_RETRY(open("/proc/cpuinfo", O_RDONLY));
        if (fd < 0) {
            SkDebugf("Could not open /proc/cpuinfo: %s\n", strerror(errno));
            break;
        }

        // Read the file. To simplify our search, we're going to place two
        // sentinel '\n' characters: one at the start of the buffer, and one at
        // the end. This means we reserve the first and last buffer bytes.
        buffer[0] = '\n';
        int size = TEMP_FAILURE_RETRY(read(fd, buffer+1, sizeof(buffer)-2));
        close(fd);

        if (size < 0) {  // should not happen
            SkDebugf("Could not read /proc/cpuinfo: %s\n", strerror(errno));
            break;
        }

        SkDebugf("START /proc/cpuinfo:\n%.*s\nEND /proc/cpuinfo\n",
                 size, buffer+1);

        // Compute buffer limit, and place final sentinel
        char* buffer_end = buffer + 1 + size;
        buffer_end[0] = '\n';

        // Now, find a line that starts with "Features", i.e. look for
        // '\nFeatures ' in our buffer.
        const char features[] = "\nFeatures\t";
        const size_t features_len = sizeof(features)-1;

        char*  line = (char*) memmem(buffer, buffer_end - buffer,
                                     features, features_len);
        if (line == nullptr) {  // Weird, no Features line, bad kernel?
            SkDebugf("Could not find a line starting with 'Features'"
              "in /proc/cpuinfo ?\n");
            break;
        }

        line += features_len;  // Skip the "\nFeatures\t" prefix

        // Find the end of the current line
        char* line_end = (char*) memchr(line, '\n', buffer_end - line);
        if (line_end == nullptr)
            line_end = buffer_end;

        // Now find an instance of 'neon' in the flags list. We want to
        // ensure it's only 'neon' and not something fancy like 'noneon'
        // so check that it follows a space.
        const char neon[] = " neon";
        const size_t neon_len = sizeof(neon)-1;
        const char* flag = (const char*) memmem(line, line_end - line,
                                                neon, neon_len);
        if (flag == nullptr)
            break;

        // Ensure it is followed by a space or a newline.
        if (flag[neon_len] != ' ' && flag[neon_len] != '\n')
            break;

        // Fine, we support Arm NEON !
        result = true;

    } while (0);

#endif  // SK_BUILD_FOR_ANDROID

    if (result) {
        SkDEBUGF(("Device supports ARM NEON instructions!\n"));
    } else {
        SkDEBUGF(("Device does NOT support ARM NEON instructions!\n"));
    }
    return result;
}

static pthread_once_t  sOnce;
static bool            sHasArmNeon;

// called through pthread_once()
void sk_cpu_arm_probe_features(void) {
    sHasArmNeon = sk_cpu_arm_check_neon();
}

bool sk_cpu_arm_has_neon(void) {
    pthread_once(&sOnce, sk_cpu_arm_probe_features);
    return sHasArmNeon;
}

#endif // SK_ARM_NEON_IS_DYNAMIC