Use kAudioObjectPropertyElementMaster on macOS for compatibility
[openal-soft.git] / core / cpu_caps.cpp
blobddc318162add7ff1e3fad8cd2b2bd790166e8d35
2 #include "config.h"
3 #include "config_simd.h"
5 #include "cpu_caps.h"
7 #if defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64))
8 #define WIN32_LEAN_AND_MEAN
9 #include <windows.h>
10 #ifndef PF_ARM_NEON_INSTRUCTIONS_AVAILABLE
11 #define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19
12 #endif
13 #endif
15 #if defined(HAVE_CPUID_H)
16 #include <cpuid.h>
17 #elif defined(HAVE_INTRIN_H)
18 #include <intrin.h>
19 #endif
21 #include <algorithm>
22 #include <array>
23 #include <cctype>
24 #include <string>
27 int CPUCapFlags{0};
29 namespace {
31 #if defined(HAVE_GCC_GET_CPUID) \
32 && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
33 using reg_type = unsigned int;
34 inline std::array<reg_type,4> get_cpuid(unsigned int f)
36 std::array<reg_type,4> ret{};
37 __get_cpuid(f, ret.data(), &ret[1], &ret[2], &ret[3]);
38 return ret;
40 #define CAN_GET_CPUID
41 #elif defined(HAVE_CPUID_INTRINSIC) \
42 && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
43 using reg_type = int;
44 inline std::array<reg_type,4> get_cpuid(unsigned int f)
46 std::array<reg_type,4> ret{};
47 (__cpuid)(ret.data(), f);
48 return ret;
50 #define CAN_GET_CPUID
51 #endif
53 } // namespace
55 std::optional<CPUInfo> GetCPUInfo()
57 CPUInfo ret;
59 #ifdef CAN_GET_CPUID
60 auto cpuregs = get_cpuid(0);
61 if(cpuregs[0] == 0)
62 return std::nullopt;
64 const reg_type maxfunc{cpuregs[0]};
66 cpuregs = get_cpuid(0x80000000);
67 const reg_type maxextfunc{cpuregs[0]};
69 ret.mVendor.append(reinterpret_cast<char*>(&cpuregs[1]), 4);
70 ret.mVendor.append(reinterpret_cast<char*>(&cpuregs[3]), 4);
71 ret.mVendor.append(reinterpret_cast<char*>(&cpuregs[2]), 4);
72 auto iter_end = std::remove(ret.mVendor.begin(), ret.mVendor.end(), '\0');
73 iter_end = std::unique(ret.mVendor.begin(), iter_end,
74 [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); });
75 ret.mVendor.erase(iter_end, ret.mVendor.end());
76 if(!ret.mVendor.empty() && std::isspace(ret.mVendor.back()))
77 ret.mVendor.pop_back();
78 if(!ret.mVendor.empty() && std::isspace(ret.mVendor.front()))
79 ret.mVendor.erase(ret.mVendor.begin());
81 if(maxextfunc >= 0x80000004)
83 cpuregs = get_cpuid(0x80000002);
84 ret.mName.append(reinterpret_cast<char*>(cpuregs.data()), 16);
85 cpuregs = get_cpuid(0x80000003);
86 ret.mName.append(reinterpret_cast<char*>(cpuregs.data()), 16);
87 cpuregs = get_cpuid(0x80000004);
88 ret.mName.append(reinterpret_cast<char*>(cpuregs.data()), 16);
89 iter_end = std::remove(ret.mName.begin(), ret.mName.end(), '\0');
90 iter_end = std::unique(ret.mName.begin(), iter_end,
91 [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); });
92 ret.mName.erase(iter_end, ret.mName.end());
93 if(!ret.mName.empty() && std::isspace(ret.mName.back()))
94 ret.mName.pop_back();
95 if(!ret.mName.empty() && std::isspace(ret.mName.front()))
96 ret.mName.erase(ret.mName.begin());
99 if(maxfunc >= 1)
101 cpuregs = get_cpuid(1);
102 if((cpuregs[3]&(1<<25)))
103 ret.mCaps |= CPU_CAP_SSE;
104 if((ret.mCaps&CPU_CAP_SSE) && (cpuregs[3]&(1<<26)))
105 ret.mCaps |= CPU_CAP_SSE2;
106 if((ret.mCaps&CPU_CAP_SSE2) && (cpuregs[2]&(1<<0)))
107 ret.mCaps |= CPU_CAP_SSE3;
108 if((ret.mCaps&CPU_CAP_SSE3) && (cpuregs[2]&(1<<19)))
109 ret.mCaps |= CPU_CAP_SSE4_1;
112 #else
114 /* Assume support for whatever's supported if we can't check for it */
115 #if HAVE_SSE4_1
116 #warning "Assuming SSE 4.1 run-time support!"
117 ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
118 #elif HAVE_SSE3
119 #warning "Assuming SSE 3 run-time support!"
120 ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
121 #elif HAVE_SSE2
122 #warning "Assuming SSE 2 run-time support!"
123 ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2;
124 #elif HAVE_SSE
125 #warning "Assuming SSE run-time support!"
126 ret.mCaps |= CPU_CAP_SSE;
127 #endif
128 #endif /* CAN_GET_CPUID */
130 #if HAVE_NEON
131 #ifdef __ARM_NEON
132 ret.mCaps |= CPU_CAP_NEON;
133 #elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64))
134 if(IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE))
135 ret.mCaps |= CPU_CAP_NEON;
136 #else
137 #warning "Assuming NEON run-time support!"
138 ret.mCaps |= CPU_CAP_NEON;
139 #endif
140 #endif
142 return ret;