OSDN Git Service

Clean up MUtils::CPUFetaures code.
[mutilities/MUtilities.git] / src / CPUFeatures_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2016 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //Win32 API
23 #define WIN32_LEAN_AND_MEAN 1
24 #include <Windows.h>
25
26 //MUtils
27 #include <MUtils/CPUFeatures.h>
28 #include <MUtils/OSSupport.h>
29 #include "Utils_Win32.h"
30
31 #define MY_CPUID(X,Y) __cpuid(((int*)(X)), ((int)(Y)))
32 #define CHECK_VENDOR(X,Y,Z) (_stricmp((X), (Y)) ? 0U : (Z));
33 #define CHECK_FLAG(X,Y,Z) (((X) & (Y)) ? (Z) : 0U)
34
35 MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
36 {
37         const OS::ArgumentMap &args = OS::arguments();
38         typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
39         static const quint32 FLAGS_X64 = (FLAG_MMX | FLAG_SSE | FLAG_SSE2);
40
41         cpu_info_t  features;
42         SYSTEM_INFO systemInfo;
43         uint32_t    cpuInfo[4];
44
45         //Initialize variables to zero
46         memset(&features,   0, sizeof(cpu_info_t));
47         memset(&systemInfo, 0, sizeof(SYSTEM_INFO));
48         memset(cpuInfo,     0, sizeof(cpuInfo));
49
50         //Detect the CPU identifier string
51         MY_CPUID(&cpuInfo[0], 0);
52         memcpy(&features.idstr[0U * sizeof(uint32_t)], &cpuInfo[1], sizeof(uint32_t));
53         memcpy(&features.idstr[1U * sizeof(uint32_t)], &cpuInfo[3], sizeof(uint32_t));
54         memcpy(&features.idstr[2U * sizeof(uint32_t)], &cpuInfo[2], sizeof(uint32_t));
55         features.idstr[3U * sizeof(uint32_t)] = '\0';
56         features.vendor |= CHECK_VENDOR(features.idstr, "GenuineIntel", VENDOR_INTEL);
57         features.vendor |= CHECK_VENDOR(features.idstr, "AuthenticAMD", VENDOR_AMD);
58
59         //Detect the CPU model and feature flags
60         if(cpuInfo[0] >= 1)
61         {
62                 MY_CPUID(&cpuInfo[0], 1);
63                 features.features |= CHECK_FLAG(cpuInfo[3], 0x00008000, FLAG_CMOV);
64                 features.features |= CHECK_FLAG(cpuInfo[3], 0x00800000, FLAG_MMX);
65                 features.features |= CHECK_FLAG(cpuInfo[3], 0x02000000, FLAG_SSE);
66                 features.features |= CHECK_FLAG(cpuInfo[3], 0x04000000, FLAG_SSE2);
67                 features.features |= CHECK_FLAG(cpuInfo[2], 0x00000001, FLAG_SSE3);
68                 features.features |= CHECK_FLAG(cpuInfo[2], 0x00000200, FLAG_SSSE3);
69                 features.features |= CHECK_FLAG(cpuInfo[2], 0x00080000, FLAG_SSE4);
70                 features.features |= CHECK_FLAG(cpuInfo[2], 0x00100000, FLAG_SSE42);
71
72                 //Check for AVX
73                 if ((cpuInfo[2] & 0x18000000) == 0x18000000)
74                 {
75                         if((_xgetbv(0) & 0x6ui64) == 0x6ui64) /*AVX requires OS support!*/
76                         {
77                                 features.features |= FLAG_AVX;
78                         }
79                 }
80
81                 //Compute the CPU stepping, model and family
82                 features.stepping = cpuInfo[0] & 0xf;
83                 features.model    = ((cpuInfo[0] >> 4) & 0xf) + (((cpuInfo[0] >> 16) & 0xf) << 4);
84                 features.family   = ((cpuInfo[0] >> 8) & 0xf) + ((cpuInfo[0] >> 20) & 0xff);
85         }
86
87         //Read the CPU "brand" string
88         MY_CPUID(&cpuInfo[0], 0x80000000);
89         const uint32_t nExIds = qBound(0x80000000, cpuInfo[0], 0x80000004);
90         for(uint32_t i = 0x80000002; i <= nExIds; ++i)
91         {
92                 MY_CPUID(&cpuInfo[0], i);
93                 memcpy(&features.brand[(i - 0x80000002) * sizeof(cpuInfo)], &cpuInfo[0], sizeof(cpuInfo));
94         }
95         features.brand[sizeof(features.brand) - 1] = '\0';
96
97         //Detect 64-Bit processors
98 #if (!(defined(_M_X64) || defined(_M_IA64)))
99         const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process"));
100         if(isWow64ProcessPtr)
101         {
102                 BOOL x64flag = FALSE;
103                 if(isWow64ProcessPtr(GetCurrentProcess(), &x64flag))
104                 {
105                         if (x64flag)
106                         {
107                                 features.x64 = true;
108                                 features.features |= FLAGS_X64; /*x86_64 implies SSE2*/
109                         }
110                 }
111         }
112 #else
113         features.x64 = true;
114         features.features |= FLAGS_X64;
115 #endif
116
117         //Make sure that (at least) the MMX flag has been set!
118         if (!(features.features & FLAG_MMX))
119         {
120                 qWarning("Warning: CPU does not seem to support MMX. Take care!\n");
121                 features.features = 0;
122         }
123
124         //Count the number of available(!) CPU cores
125         DWORD_PTR procAffinity, sysAffinity;
126         if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity))
127         {
128                 for(DWORD_PTR mask = 1; mask; mask <<= 1)
129                 {
130                         features.count += ((sysAffinity & mask) ? (1) : (0));
131                 }
132         }
133         if(features.count < 1)
134         {
135                 GetNativeSystemInfo(&systemInfo);
136                 features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL);
137         }
138
139         //Apply manual CPU overwrites
140         bool userFlag = false;
141         if (args.contains(QLatin1String("cpu-no-simd")))   { userFlag = true; features.features = 0U; }
142         if (args.contains(QLatin1String("cpu-no-vendor"))) { userFlag = true; features.vendor   = 0U; }
143         if (args.contains(QLatin1String("cpu-no-x64")))    { userFlag = true; features.x64      = 0U; }
144         if(userFlag)
145         {
146                 qWarning("CPU flags overwritten by user-defined parameters. Take care!\n");
147         }
148
149         return features;
150 }