Clean up MUtils::CPUFetaures code.
This commit is contained in:
parent
1cff20a3ef
commit
37e7f72069
@ -19,6 +19,13 @@
|
||||
// http://www.gnu.org/licenses/lgpl-2.1.txt
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief This file contains function for detecting information about the CPU
|
||||
*
|
||||
* Call the MUtils::CPUFetaures::detect() to detect information about the processor, which will be returned in a `MUtils::CPUFetaures::cpu_info_t` struct.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//MUtils
|
||||
@ -29,33 +36,52 @@
|
||||
|
||||
namespace MUtils
|
||||
{
|
||||
/**
|
||||
* \brief This namespace contains functions and constants for detecting CPU information
|
||||
*
|
||||
* Call the detect() to detect information about the processor, which will be returned in a `cpu_info_t` struct.
|
||||
*/
|
||||
namespace CPUFetaures
|
||||
{
|
||||
//CPU flags
|
||||
static const quint32 FLAG_MMX = 0x01;
|
||||
static const quint32 FLAG_SSE = 0x02;
|
||||
static const quint32 FLAG_SSE2 = 0x04;
|
||||
static const quint32 FLAG_SSE3 = 0x08;
|
||||
static const quint32 FLAG_SSSE3 = 0x10;
|
||||
static const quint32 FLAG_SSE4 = 0x20;
|
||||
static const quint32 FLAG_SSE42 = 0x40;
|
||||
static const quint32 FLAG_AVX = 0x80;
|
||||
// CPU vendor flag
|
||||
static const uint8_t VENDOR_INTEL = 0x01U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *Intel*
|
||||
static const uint8_t VENDOR_AMD = 0x02U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *AMD*
|
||||
|
||||
//CPU features
|
||||
typedef struct _cpu_info_t
|
||||
// CPU feature flag
|
||||
static const quint32 FLAG_CMOV = 0x001U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *CMOV* instruction
|
||||
static const quint32 FLAG_MMX = 0x002U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *MMX* instruction set extension
|
||||
static const quint32 FLAG_SSE = 0x004U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE* instruction set extension
|
||||
static const quint32 FLAG_SSE2 = 0x008U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE2* instruction set extension
|
||||
static const quint32 FLAG_SSE3 = 0x010U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE3* instruction set extension
|
||||
static const quint32 FLAG_SSSE3 = 0x020U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSSE3* instruction set extension
|
||||
static const quint32 FLAG_SSE4 = 0x030U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.1* instruction set extension
|
||||
static const quint32 FLAG_SSE42 = 0x080U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.2* instruction set extension
|
||||
static const quint32 FLAG_AVX = 0x100U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *AVX* instruction set extension
|
||||
|
||||
/**
|
||||
* \brief Struct to hold information about the CPU
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
quint32 family;
|
||||
quint32 model;
|
||||
quint32 stepping;
|
||||
quint32 count;
|
||||
quint32 features;
|
||||
bool x64;
|
||||
bool intel;
|
||||
char vendor[0x40];
|
||||
char brand[0x40];
|
||||
quint32 family; ///< CPU *family* indicator, which specifies the processor "generation" to which the CPU belongs
|
||||
quint32 model; ///< CPU *model* indicator, which is used to distinguish processor "variants" within a generation
|
||||
quint32 stepping; ///< CPU *stepping* indicator, which is used to distinguish "revisions" of a certain processor model
|
||||
quint32 count; ///< The number of available (logical) processors
|
||||
quint32 features; ///< CPU *feature* flags, indicating suppoprt for extended instruction sets; all flags are OR-combined
|
||||
bool x64; ///< Indicates that the processor and the operating system support 64-Bit (AMD64/EM64T)
|
||||
uint8_t vendor; ///< CPU *vendor* flag; might be zero, if vendor is unknown
|
||||
char idstr[13]; ///< CPU *identifier* string, exactly 12 characters (e.g. "GenuineIntel" or "AuthenticAMD")
|
||||
char brand[48]; ///< CPU *brand* string, up to 48 characters (e.g. "Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz")
|
||||
}
|
||||
cpu_info_t;
|
||||
|
||||
/**
|
||||
* \brief Detect processor information
|
||||
*
|
||||
* Detects information about the CPU on which the application is running, including CPU vendor, identifier string, feature flags (MMX, SSE, AVX, etc) as well as the CPU core count.
|
||||
*
|
||||
* \return The function returns a `cpu_info_t` struct containing the detected information about the CPU.
|
||||
*/
|
||||
MUTILS_API cpu_info_t detect(void);
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ class QProcess;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* \brief Global MUtils namespace
|
||||
*/
|
||||
namespace MUtils
|
||||
{
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@
|
||||
*
|
||||
* The public API of the *MUtilities* library is defined in the following header files (select file for details):
|
||||
* - **Global.h** – miscellaneous useful functions
|
||||
* - **CPUFeatures.h** – functions for detection information about the CPU
|
||||
*
|
||||
*
|
||||
* # Example
|
||||
|
@ -28,76 +28,73 @@
|
||||
#include <MUtils/OSSupport.h>
|
||||
#include "Utils_Win32.h"
|
||||
|
||||
#define MY_CPUID(X,Y) __cpuid(((int*)(X)), ((int)(Y)))
|
||||
#define CHECK_VENDOR(X,Y,Z) (_stricmp((X), (Y)) ? 0U : (Z));
|
||||
#define CHECK_FLAG(X,Y,Z) (((X) & (Y)) ? (Z) : 0U)
|
||||
|
||||
MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
|
||||
{
|
||||
const OS::ArgumentMap &args = OS::arguments();
|
||||
typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
|
||||
static const quint32 FLAGS_X64 = (FLAG_MMX | FLAG_SSE | FLAG_SSE2);
|
||||
|
||||
cpu_info_t features;
|
||||
SYSTEM_INFO systemInfo;
|
||||
int CPUInfo[4] = {-1};
|
||||
char CPUIdentificationString[0x40];
|
||||
char CPUBrandString[0x40];
|
||||
uint32_t cpuInfo[4];
|
||||
|
||||
//Initialize variables to zero
|
||||
memset(&features, 0, sizeof(cpu_info_t));
|
||||
memset(&systemInfo, 0, sizeof(SYSTEM_INFO));
|
||||
memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString));
|
||||
memset(CPUBrandString, 0, sizeof(CPUBrandString));
|
||||
memset(cpuInfo, 0, sizeof(cpuInfo));
|
||||
|
||||
__cpuid(CPUInfo, 0);
|
||||
memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int));
|
||||
memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int));
|
||||
memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int));
|
||||
features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0);
|
||||
strncpy_s(features.vendor, 0x40, CPUIdentificationString, _TRUNCATE);
|
||||
//Detect the CPU identifier string
|
||||
MY_CPUID(&cpuInfo[0], 0);
|
||||
memcpy(&features.idstr[0U * sizeof(uint32_t)], &cpuInfo[1], sizeof(uint32_t));
|
||||
memcpy(&features.idstr[1U * sizeof(uint32_t)], &cpuInfo[3], sizeof(uint32_t));
|
||||
memcpy(&features.idstr[2U * sizeof(uint32_t)], &cpuInfo[2], sizeof(uint32_t));
|
||||
features.idstr[3U * sizeof(uint32_t)] = '\0';
|
||||
features.vendor |= CHECK_VENDOR(features.idstr, "GenuineIntel", VENDOR_INTEL);
|
||||
features.vendor |= CHECK_VENDOR(features.idstr, "AuthenticAMD", VENDOR_AMD);
|
||||
|
||||
if(CPUInfo[0] >= 1)
|
||||
//Detect the CPU model and feature flags
|
||||
if(cpuInfo[0] >= 1)
|
||||
{
|
||||
__cpuid(CPUInfo, 1);
|
||||
if(CPUInfo[3] & 0x00800000) features.features |= FLAG_MMX;
|
||||
if(CPUInfo[3] & 0x02000000) features.features |= FLAG_SSE;
|
||||
if(CPUInfo[3] & 0x04000000) features.features |= FLAG_SSE2;
|
||||
if(CPUInfo[2] & 0x00000001) features.features |= FLAG_SSE3;
|
||||
if(CPUInfo[2] & 0x00000200) features.features |= FLAG_SSSE3;
|
||||
if(CPUInfo[2] & 0x00080000) features.features |= FLAG_SSE4;
|
||||
if(CPUInfo[2] & 0x00100000) features.features |= FLAG_SSE42;
|
||||
if ((CPUInfo[2] & 0x18000000) == 0x18000000)
|
||||
MY_CPUID(&cpuInfo[0], 1);
|
||||
features.features |= CHECK_FLAG(cpuInfo[3], 0x00008000, FLAG_CMOV);
|
||||
features.features |= CHECK_FLAG(cpuInfo[3], 0x00800000, FLAG_MMX);
|
||||
features.features |= CHECK_FLAG(cpuInfo[3], 0x02000000, FLAG_SSE);
|
||||
features.features |= CHECK_FLAG(cpuInfo[3], 0x04000000, FLAG_SSE2);
|
||||
features.features |= CHECK_FLAG(cpuInfo[2], 0x00000001, FLAG_SSE3);
|
||||
features.features |= CHECK_FLAG(cpuInfo[2], 0x00000200, FLAG_SSSE3);
|
||||
features.features |= CHECK_FLAG(cpuInfo[2], 0x00080000, FLAG_SSE4);
|
||||
features.features |= CHECK_FLAG(cpuInfo[2], 0x00100000, FLAG_SSE42);
|
||||
|
||||
//Check for AVX
|
||||
if ((cpuInfo[2] & 0x18000000) == 0x18000000)
|
||||
{
|
||||
if((_xgetbv(0) & 0x6ui64) == 0x6ui64) /*AVX requires OS support!*/
|
||||
{
|
||||
features.features |= FLAG_AVX;
|
||||
}
|
||||
}
|
||||
features.stepping = CPUInfo[0] & 0xf;
|
||||
features.model = ((CPUInfo[0] >> 4) & 0xf) + (((CPUInfo[0] >> 16) & 0xf) << 4);
|
||||
features.family = ((CPUInfo[0] >> 8) & 0xf) + ((CPUInfo[0] >> 20) & 0xff);
|
||||
|
||||
//Compute the CPU stepping, model and family
|
||||
features.stepping = cpuInfo[0] & 0xf;
|
||||
features.model = ((cpuInfo[0] >> 4) & 0xf) + (((cpuInfo[0] >> 16) & 0xf) << 4);
|
||||
features.family = ((cpuInfo[0] >> 8) & 0xf) + ((cpuInfo[0] >> 20) & 0xff);
|
||||
}
|
||||
|
||||
__cpuid(CPUInfo, 0x80000000);
|
||||
int nExIds = qMax<int>(qMin<int>(CPUInfo[0], 0x80000004), 0x80000000);
|
||||
|
||||
for(int i = 0x80000002; i <= nExIds; ++i)
|
||||
//Read the CPU "brand" string
|
||||
MY_CPUID(&cpuInfo[0], 0x80000000);
|
||||
const uint32_t nExIds = qBound(0x80000000, cpuInfo[0], 0x80000004);
|
||||
for(uint32_t i = 0x80000002; i <= nExIds; ++i)
|
||||
{
|
||||
__cpuid(CPUInfo, i);
|
||||
switch(i)
|
||||
{
|
||||
case 0x80000002:
|
||||
memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
|
||||
break;
|
||||
case 0x80000003:
|
||||
memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
|
||||
break;
|
||||
case 0x80000004:
|
||||
memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
|
||||
break;
|
||||
}
|
||||
MY_CPUID(&cpuInfo[0], i);
|
||||
memcpy(&features.brand[(i - 0x80000002) * sizeof(cpuInfo)], &cpuInfo[0], sizeof(cpuInfo));
|
||||
}
|
||||
features.brand[sizeof(features.brand) - 1] = '\0';
|
||||
|
||||
strncpy_s(features.brand, 0x40, CPUBrandString, _TRUNCATE);
|
||||
|
||||
if(strlen(features.brand) < 1) strncpy_s(features.brand, 0x40, "Unknown", _TRUNCATE);
|
||||
if(strlen(features.vendor) < 1) strncpy_s(features.vendor, 0x40, "Unknown", _TRUNCATE);
|
||||
|
||||
//Detect 64-Bit processors
|
||||
#if (!(defined(_M_X64) || defined(_M_IA64)))
|
||||
const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process"));
|
||||
if(isWow64ProcessPtr)
|
||||
@ -105,24 +102,26 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
|
||||
BOOL x64flag = FALSE;
|
||||
if(isWow64ProcessPtr(GetCurrentProcess(), &x64flag))
|
||||
{
|
||||
if(x64flag) features.x64 = true;
|
||||
if (x64flag)
|
||||
{
|
||||
features.x64 = true;
|
||||
features.features |= FLAGS_X64; /*x86_64 implies SSE2*/
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
features.x64 = true;
|
||||
features.features |= FLAGS_X64;
|
||||
#endif
|
||||
|
||||
if (features.x64)
|
||||
{
|
||||
features.features |= (FLAG_MMX | FLAG_SSE | FLAG_SSE2); /*x86_64 implies SSE2*/
|
||||
}
|
||||
|
||||
//Make sure that (at least) the MMX flag has been set!
|
||||
if (!(features.features & FLAG_MMX))
|
||||
{
|
||||
qWarning("Warning: CPU does not seem to support MMX. Take care!\n");
|
||||
features.features = 0;
|
||||
}
|
||||
|
||||
//Count the number of available(!) CPU cores
|
||||
DWORD_PTR procAffinity, sysAffinity;
|
||||
if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity))
|
||||
{
|
||||
@ -137,11 +136,11 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
|
||||
features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL);
|
||||
}
|
||||
|
||||
//Apply manual CPU overwrites
|
||||
bool userFlag = false;
|
||||
if(args.contains("force-cpu-no-64bit")) { userFlag = true; features.x64 = false; }
|
||||
if(args.contains("force-cpu-no-sse" )) { userFlag = true; features.features &= (~(FLAG_SSE | FLAG_SSE2 | FLAG_SSE3 | FLAG_SSSE3 | FLAG_SSE4 | FLAG_SSE42)); }
|
||||
if(args.contains("force-cpu-no-intel")) { userFlag = true; features.intel = false; }
|
||||
|
||||
if (args.contains(QLatin1String("cpu-no-simd"))) { userFlag = true; features.features = 0U; }
|
||||
if (args.contains(QLatin1String("cpu-no-vendor"))) { userFlag = true; features.vendor = 0U; }
|
||||
if (args.contains(QLatin1String("cpu-no-x64"))) { userFlag = true; features.x64 = 0U; }
|
||||
if(userFlag)
|
||||
{
|
||||
qWarning("CPU flags overwritten by user-defined parameters. Take care!\n");
|
||||
|
Loading…
Reference in New Issue
Block a user