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