You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/AutoDetectSpec.cpp

1100 lines
33 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(WIN32) || defined(WIN64)
#include <wbemidl.h>
#include <intrin.h>
#include <d3d9.h>
#include <ddraw.h>
#include <dxgi.h>
#include <d3d11.h>
#include <map>
#include <AzFramework/Archive/IArchive.h>
#include <IConsole.h>
#include <StringUtils.h>
#include "System.h"
#include "AutoDetectSpec.h"
// both function live in CPUDetect.cpp
bool IsAMD();
bool IsIntel();
static void TrimExcessiveWhiteSpaces(char* pStr)
{
size_t len(strlen(pStr));
bool remove(true);
for (size_t i(0); i < len; ++i)
{
if (pStr[i] == ' ' && remove)
{
size_t newlen(len - 1);
size_t j(i + 1);
for (; j < len && pStr[j] == ' '; ++j)
{
--newlen;
}
size_t ii(i);
for (; j < len + 1; ++j, ++ii)
{
pStr[ii] = pStr[j];
}
assert(newlen == strlen(pStr));
len = newlen;
remove = false;
}
else
{
remove = pStr[i] == ' ';
}
}
if (len > 0 && pStr[len - 1] == ' ')
{
pStr[len - 1] = '\0';
}
}
static void GetCPUName(char* pName, size_t bufferSize)
{
if (!pName || !bufferSize)
{
return;
}
char name[12 * 4 + 1];
int CPUInfo[4];
__cpuid(CPUInfo, 0x80000000);
if (CPUInfo[0] >= 0x80000004)
{
__cpuid(CPUInfo, 0x80000002);
((int*)name)[0] = CPUInfo[0];
((int*)name)[1] = CPUInfo[1];
((int*)name)[2] = CPUInfo[2];
((int*)name)[3] = CPUInfo[3];
__cpuid(CPUInfo, 0x80000003);
((int*)name)[4] = CPUInfo[0];
((int*)name)[5] = CPUInfo[1];
((int*)name)[6] = CPUInfo[2];
((int*)name)[7] = CPUInfo[3];
__cpuid(CPUInfo, 0x80000004);
((int*)name)[8] = CPUInfo[0];
((int*)name)[9] = CPUInfo[1];
((int*)name)[10] = CPUInfo[2];
((int*)name)[11] = CPUInfo[3];
name[48] = '\0';
}
else
{
name[0] = '\0';
}
int ret(azsnprintf(pName, bufferSize, name));
if (ret >= bufferSize || ret < 0)
{
pName[bufferSize - 1] = '\0';
}
}
void Win32SysInspect::GetOS(SPlatformInfo::EWinVersion& ver, bool& is64Bit, char* pName, size_t bufferSize)
{
ver = SPlatformInfo::WinUndetected;
is64Bit = false;
if (pName && bufferSize)
{
pName[0] = '\0';
}
//GetVersionEx was changed to work based on how the application is manifest
//meaning we have to specifically state that this application supports
//Windows 10, Windows 8.1 etc if we want GetVersionEx to return those version numbers.
//RtlGetVersion does not require a manifest.
auto RtlGetVersion = reinterpret_cast<LONG(WINAPI*)(LPOSVERSIONINFOEXW)>(GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"));
AZ_Assert(RtlGetVersion, "Failed to get address to RtlGetVersion from ntdll.dll");
RTL_OSVERSIONINFOEXW sysInfo;
sysInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
if (RtlGetVersion(&sysInfo) == 0)
{
if (sysInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
if (sysInfo.dwMajorVersion == 5)
{
if (sysInfo.dwMinorVersion == 0)
{
ver = SPlatformInfo::Win2000;
}
else if (sysInfo.dwMinorVersion == 1)
{
ver = SPlatformInfo::WinXP;
}
else if (sysInfo.dwMinorVersion == 2)
{
if (sysInfo.wProductType == VER_NT_WORKSTATION)
{
ver = SPlatformInfo::WinXP; // 64 bit windows actually but this will be detected later anyway
}
else if (sysInfo.wProductType == VER_NT_SERVER || sysInfo.wProductType == VER_NT_DOMAIN_CONTROLLER)
{
ver = SPlatformInfo::WinSrv2003;
}
}
}
else if (sysInfo.dwMajorVersion == 6)
{
if (sysInfo.dwMinorVersion == 0)
{
ver = SPlatformInfo::WinVista;
}
else if (sysInfo.dwMinorVersion == 1)
{
ver = SPlatformInfo::Win7;
}
else if (sysInfo.dwMinorVersion == 2)
{
ver = SPlatformInfo::Win8;
}
else if (sysInfo.dwMinorVersion == 3)
{
ver = SPlatformInfo::Win81;
}
}
else if (sysInfo.dwMajorVersion == 10)
{
ver = SPlatformInfo::Win10;
}
}
typedef BOOL (WINAPI * FP_GetSystemWow64Directory)(LPSTR, UINT);
FP_GetSystemWow64Directory pgsw64d((FP_GetSystemWow64Directory) GetProcAddress(GetModuleHandle("kernel32"), "GetSystemWow64DirectoryA"));
if (pgsw64d)
{
char str[MAX_PATH];
if (!pgsw64d(str, sizeof(str)))
{
is64Bit = GetLastError() != ERROR_CALL_NOT_IMPLEMENTED;
}
else
{
is64Bit = true;
}
}
if (pName && bufferSize)
{
const char* windowsVersionText(0);
switch (ver)
{
case SPlatformInfo::Win2000:
windowsVersionText = "Windows 2000";
break;
case SPlatformInfo::WinXP:
windowsVersionText = "Windows XP";
break;
case SPlatformInfo::WinSrv2003:
windowsVersionText = "Windows Server 2003";
break;
case SPlatformInfo::WinVista:
windowsVersionText = "Windows Vista";
break;
case SPlatformInfo::Win7:
windowsVersionText = "Windows 7";
break;
case SPlatformInfo::Win8:
windowsVersionText = "Windows 8";
break;
case SPlatformInfo::Win81:
windowsVersionText = "Windows 8.1";
break;
case SPlatformInfo::Win10:
windowsVersionText = "Windows 10";
break;
default:
windowsVersionText = "Windows";
break;
}
char sptext[32];
sptext[0] = '\0';
if (sysInfo.wServicePackMajor > 0)
{
azsnprintf(sptext, sizeof(sptext), "SP %d ", sysInfo.wServicePackMajor);
}
int ret(azsnprintf(pName, bufferSize, "%s %s %s(build %d.%d.%d)", windowsVersionText, is64Bit ? "64 bit" : "32 bit",
sptext, sysInfo.dwMajorVersion, sysInfo.dwMinorVersion, sysInfo.dwBuildNumber));
if (ret >= bufferSize || ret < 0)
{
pName[bufferSize - 1] = '\0';
}
}
}
}
bool Win32SysInspect::IsVistaKB940105Required()
{
#if defined(WIN32) && !defined(WIN64)
OSVERSIONINFO osv;
memset(&osv, 0, sizeof(osv));
osv.dwOSVersionInfoSize = sizeof(osv);
GetVersionEx(&osv);
if (osv.dwMajorVersion != 6 || osv.dwMinorVersion != 0 || (osv.dwBuildNumber > 6000))
{
// This QFE only ever applies to Windows Vista RTM. Windows Vista SP1 already has this fix,
// and earlier versions of Windows do not implement WDDM
return false;
}
//MEMORYSTATUSEX mex;
//memset(&mex, 0, sizeof(mex));
//mex.dwLength = sizeof(mex);
//GlobalMemoryStatusEx(&mex);
//if (mex.ullTotalVirtual >= 4294836224)
//{
// // If there is 4 GB of VA space total for this process, then we are a
// // 32-bit Large Address Aware application running on a Windows 64-bit OS.
// // We could be a 32-bit Large Address Aware application running on a
// // Windows 32-bit OS and get up to 3 GB, but that has stability implications.
// // Therefore, we recommend the QFE for all 32-bit versions of the OS.
// // No need for the fix unless the game is pushing 4 GB of VA
// return false;
//}
const char* sysFile = "dxgkrnl.sys";
// Ensure we are checking the system copy of the file
char sysPath[MAX_PATH];
GetSystemDirectory(sysPath, sizeof(sysPath));
cry_strcat(sysPath, "\\drivers\\");
cry_strcat(sysPath, sysFile);
char buf[2048];
if (!GetFileVersionInfo(sysPath, 0, sizeof(buf), buf))
{
// This should never happen, but we'll assume it's a newer .sys file since we've
// narrowed the test to a Windows Vista RTM OS.
return false;
}
VS_FIXEDFILEINFO* ver;
UINT size;
if (!VerQueryValue(buf, "\\", (void**) &ver, &size) || size != sizeof(VS_FIXEDFILEINFO) || ver->dwSignature != 0xFEEF04BD)
{
// This should never happen, but we'll assume it's a newer .sys file since we've
// narrowed the test to a Windows Vista RTM OS.
return false;
}
// File major.minor.build.qfe version comparison
// WORD major = HIWORD( ver->dwFileVersionMS ); WORD minor = LOWORD( ver->dwFileVersionMS );
// WORD build = HIWORD( ver->dwFileVersionLS ); WORD qfe = LOWORD( ver->dwFileVersionLS );
if (ver->dwFileVersionMS > MAKELONG(0, 6) || (ver->dwFileVersionMS == MAKELONG(0, 6) && ver->dwFileVersionLS >= MAKELONG(20648, 6000)))
{
// QFE fix version of dxgkrnl.sys is 6.0.6000.20648
return false;
}
return true;
#else
return false; // The QFE is not required for a 64-bit native application as it has 8 TB of VA
#endif
}
static void GetSystemMemory(uint64& totSysMem)
{
typedef BOOL (WINAPI * FP_GlobalMemoryStatusEx)(LPMEMORYSTATUSEX);
FP_GlobalMemoryStatusEx pgmsex((FP_GlobalMemoryStatusEx) GetProcAddress(GetModuleHandle("kernel32"), "GlobalMemoryStatusEx"));
if (pgmsex)
{
MEMORYSTATUSEX memStats;
memStats.dwLength = sizeof(memStats);
if (pgmsex(&memStats))
{
totSysMem = memStats.ullTotalPhys;
}
else
{
totSysMem = 0;
}
}
else
{
MEMORYSTATUS memStats;
memStats.dwLength = sizeof(memStats);
GlobalMemoryStatus(&memStats);
totSysMem = memStats.dwTotalPhys;
}
}
static bool IsVistaOrAbove()
{
typedef BOOL (WINAPI * FP_VerifyVersionInfo)(LPOSVERSIONINFOEX, DWORD, DWORDLONG);
FP_VerifyVersionInfo pvvi((FP_VerifyVersionInfo) GetProcAddress(GetModuleHandle("kernel32"), "VerifyVersionInfoA"));
if (pvvi)
{
typedef ULONGLONG (WINAPI * FP_VerSetConditionMask)(ULONGLONG, DWORD, BYTE);
FP_VerSetConditionMask pvscm((FP_VerSetConditionMask) GetProcAddress(GetModuleHandle("kernel32"), "VerSetConditionMask"));
assert(pvscm);
OSVERSIONINFOEX osvi;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 0;
osvi.wServicePackMajor = 0;
osvi.wServicePackMinor = 0;
ULONGLONG mask(0);
mask = pvscm(mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
mask = pvscm(mask, VER_MINORVERSION, VER_GREATER_EQUAL);
mask = pvscm(mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
mask = pvscm(mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
if (pvvi(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, mask))
{
return true;
}
}
return false;
}
// Preferred solution to determine the number of available CPU cores, works reliably only on WinVista/Win7 32/64 and above
// See http://msdn2.microsoft.com/en-us/library/ms686694.aspx for reasons
static void GetNumCPUCoresGlpi(unsigned int& totAvailToSystem, unsigned int& totAvailToProcess)
{
typedef BOOL (WINAPI * FP_GetLogicalProcessorInformation)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
FP_GetLogicalProcessorInformation pglpi((FP_GetLogicalProcessorInformation) GetProcAddress(GetModuleHandle("kernel32"), "GetLogicalProcessorInformation"));
if (pglpi && IsVistaOrAbove())
{
unsigned long bufferSize(0);
pglpi(0, &bufferSize);
void* pBuffer(alloca(bufferSize));
SYSTEM_LOGICAL_PROCESSOR_INFORMATION* pLogProcInfo((SYSTEM_LOGICAL_PROCESSOR_INFORMATION*) pBuffer);
if (pLogProcInfo && pglpi(pLogProcInfo, &bufferSize))
{
DWORD_PTR processAffinity, systemAffinity;
GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity);
unsigned long numEntries(bufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
for (unsigned long i(0); i < numEntries; ++i)
{
switch (pLogProcInfo[i].Relationship)
{
case RelationProcessorCore:
{
++totAvailToSystem;
if (pLogProcInfo[i].ProcessorMask & processAffinity)
{
++totAvailToProcess;
}
}
break;
default:
break;
}
}
}
}
}
class CApicExtractor
{
public:
CApicExtractor(unsigned int logProcsPerPkg = 1, unsigned int coresPerPkg = 1)
{
SetPackageTopology(logProcsPerPkg, coresPerPkg);
}
unsigned char SmtId(unsigned char apicId) const
{
return apicId & m_smtIdMask.mask;
}
unsigned char CoreId(unsigned char apicId) const
{
return (apicId & m_coreIdMask.mask) >> m_smtIdMask.width;
}
unsigned char PackageId(unsigned char apicId) const
{
return (apicId & m_pkgIdMask.mask) >> (m_smtIdMask.width + m_coreIdMask.width);
}
unsigned char PackageCoreId(unsigned char apicId) const
{
return (apicId & (m_pkgIdMask.mask | m_coreIdMask.mask)) >> m_smtIdMask.width;
}
unsigned int GetLogProcsPerPkg() const
{
return m_logProcsPerPkg;
}
unsigned int GetCoresPerPkg() const
{
return m_coresPerPkg;
}
void SetPackageTopology(unsigned int logProcsPerPkg, unsigned int coresPerPkg)
{
m_logProcsPerPkg = (unsigned char) logProcsPerPkg;
m_coresPerPkg = (unsigned char) coresPerPkg;
m_smtIdMask.width = GetMaskWidth(m_logProcsPerPkg / m_coresPerPkg);
m_coreIdMask.width = GetMaskWidth(m_coresPerPkg);
m_pkgIdMask.width = 8 - (m_smtIdMask.width + m_coreIdMask.width);
m_pkgIdMask.mask = (unsigned char) (0xFF << (m_smtIdMask.width + m_coreIdMask.width));
m_coreIdMask.mask = (unsigned char) ((0xFF << m_smtIdMask.width) ^ m_pkgIdMask.mask);
m_smtIdMask.mask = (unsigned char) ~(0xFF << m_smtIdMask.width);
}
private:
unsigned char GetMaskWidth(unsigned char maxIds) const
{
--maxIds;
unsigned char msbIdx(8);
unsigned char msbMask(0x80);
while (msbMask && !(msbMask & maxIds))
{
--msbIdx;
msbMask >>= 1;
}
return msbIdx;
}
struct IdMask
{
unsigned char width;
unsigned char mask;
};
unsigned char m_logProcsPerPkg;
unsigned char m_coresPerPkg;
IdMask m_smtIdMask;
IdMask m_coreIdMask;
IdMask m_pkgIdMask;
};
// Fallback solution for WinXP 32/64
static void GetNumCPUCoresApic(unsigned int& totAvailToSystem, unsigned int& totAvailToProcess)
{
unsigned int numLogicalPerPhysical(1);
unsigned int numCoresPerPhysical(1);
int CPUInfo[4];
__cpuid(CPUInfo, 0x00000001);
if ((CPUInfo[3] & 0x10000000) != 0) // Hyperthreading / Multicore bit set
{
numLogicalPerPhysical = (CPUInfo[1] & 0x00FF0000) >> 16;
if (IsIntel())
{
__cpuid(CPUInfo, 0x00000000);
if (CPUInfo[0] >= 0x00000004)
{
__cpuidex(CPUInfo, 4, 0);
numCoresPerPhysical = ((CPUInfo[0] & 0xFC000000) >> 26) + 1;
}
}
else if (IsAMD())
{
__cpuid(CPUInfo, 0x80000000);
if (CPUInfo[0] >= 0x80000008)
{
__cpuid(CPUInfo, 0x80000008);
if (CPUInfo[2] & 0x0000F000)
{
numCoresPerPhysical = 1 << ((CPUInfo[2] & 0x0000F000) >> 12);
}
else
{
numCoresPerPhysical = (CPUInfo[2] & 0xFF) + 1;
}
}
}
}
HANDLE hCurProcess(GetCurrentProcess());
HANDLE hCurThread(GetCurrentThread());
const int c_maxLogicalProcessors(sizeof(DWORD_PTR) * 8);
unsigned char apicIds[c_maxLogicalProcessors] = { 0 };
unsigned char items(0);
DWORD_PTR processAffinity, systemAffinity;
GetProcessAffinityMask(hCurProcess, &processAffinity, &systemAffinity);
if (systemAffinity == 1)
{
assert(numLogicalPerPhysical == 1);
apicIds[items++] = 0;
}
else
{
if (processAffinity != systemAffinity)
{
SetProcessAffinityMask(hCurProcess, systemAffinity);
}
DWORD_PTR prevThreadAffinity(0);
for (DWORD_PTR threadAffinity = 1; threadAffinity && threadAffinity <= systemAffinity; threadAffinity <<= 1)
{
if (systemAffinity & threadAffinity)
{
if (!prevThreadAffinity)
{
assert(!items);
prevThreadAffinity = SetThreadAffinityMask(hCurThread, threadAffinity);
}
else
{
assert(items > 0);
SetThreadAffinityMask(hCurThread, threadAffinity);
}
Sleep(0);
int CPUInfo2[4];
__cpuid(CPUInfo2, 0x00000001);
apicIds[items++] = (unsigned char) ((CPUInfo2[1] & 0xFF000000) >> 24);
}
}
SetProcessAffinityMask(hCurProcess, processAffinity);
SetThreadAffinityMask(hCurThread, prevThreadAffinity);
Sleep(0);
}
CApicExtractor apicExtractor(numLogicalPerPhysical, numCoresPerPhysical);
totAvailToSystem = 0;
{
unsigned char pkgCoreIds[c_maxLogicalProcessors] = { 0 };
for (unsigned int i(0); i < items; ++i)
{
unsigned int j(0);
for (; j < totAvailToSystem; ++j)
{
if (pkgCoreIds[j] == apicExtractor.PackageCoreId(apicIds[i]))
{
break;
}
}
if (j == totAvailToSystem)
{
pkgCoreIds[j] = apicExtractor.PackageCoreId(apicIds[i]);
++totAvailToSystem;
}
}
}
totAvailToProcess = 0;
{
unsigned char pkgCoreIds[c_maxLogicalProcessors] = { 0 };
for (unsigned int i(0); i < items; ++i)
{
if (processAffinity & ((DWORD_PTR) 1 << i))
{
unsigned int j(0);
for (; j < totAvailToProcess; ++j)
{
if (pkgCoreIds[j] == apicExtractor.PackageCoreId(apicIds[i]))
{
break;
}
}
if (j == totAvailToProcess)
{
pkgCoreIds[j] = apicExtractor.PackageCoreId(apicIds[i]);
++totAvailToProcess;
}
}
}
}
}
const char* Win32SysInspect::GetFeatureLevelAsString(Win32SysInspect::DXFeatureLevel featureLevel)
{
switch (featureLevel)
{
case Win32SysInspect::DXFL_Undefined:
return "unknown";
case Win32SysInspect::DXFL_9_1:
return "DX9 (SM 2.0)";
case Win32SysInspect::DXFL_9_2:
return "DX9 (SM 2.0)";
case Win32SysInspect::DXFL_9_3:
return "DX9 (SM 2.x)";
case Win32SysInspect::DXFL_10_0:
return "DX10 (SM 4.0)";
case Win32SysInspect::DXFL_10_1:
return "DX10.1 (SM 4.x)";
case Win32SysInspect::DXFL_11_0:
default:
return "DX11 (SM 5.0)";
}
}
void Win32SysInspect::GetNumCPUCores(unsigned int& totAvailToSystem, unsigned int& totAvailToProcess)
{
totAvailToSystem = 0;
totAvailToProcess = 0;
GetNumCPUCoresGlpi(totAvailToSystem, totAvailToProcess);
if (!totAvailToSystem)
{
GetNumCPUCoresApic(totAvailToSystem, totAvailToProcess);
}
}
static Win32SysInspect::DXFeatureLevel GetFeatureLevel(D3D_FEATURE_LEVEL featureLevel)
{
switch (featureLevel)
{
case D3D_FEATURE_LEVEL_9_1:
return Win32SysInspect::DXFL_9_1;
case D3D_FEATURE_LEVEL_9_2:
return Win32SysInspect::DXFL_9_2;
case D3D_FEATURE_LEVEL_9_3:
return Win32SysInspect::DXFL_9_3;
case D3D_FEATURE_LEVEL_10_0:
return Win32SysInspect::DXFL_10_0;
case D3D_FEATURE_LEVEL_10_1:
return Win32SysInspect::DXFL_10_1;
case D3D_FEATURE_LEVEL_11_0:
default:
return Win32SysInspect::DXFL_11_0;
}
}
static bool FindGPU(DXGI_ADAPTER_DESC1& adapterDesc, Win32SysInspect::DXFeatureLevel& featureLevel)
{
memset(&adapterDesc, 0, sizeof(adapterDesc));
featureLevel = Win32SysInspect::DXFL_Undefined;
if (!IsVistaOrAbove())
{
return false;
}
typedef HRESULT (WINAPI * FP_CreateDXGIFactory1)(REFIID, void**);
FP_CreateDXGIFactory1 pCDXGIF = (FP_CreateDXGIFactory1) GetProcAddress(LoadLibraryA("dxgi.dll"), "CreateDXGIFactory1");
IDXGIFactory1* pFactory = 0;
if (pCDXGIF && SUCCEEDED(pCDXGIF(__uuidof(IDXGIFactory1), (void**) &pFactory)) && pFactory)
{
typedef HRESULT (WINAPI * FP_D3D11CreateDevice)(IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT, CONST D3D_FEATURE_LEVEL*, UINT, UINT, ID3D11Device**, D3D_FEATURE_LEVEL*, ID3D11DeviceContext**);
FP_D3D11CreateDevice pD3D11CD = (FP_D3D11CreateDevice) GetProcAddress(LoadLibraryA("d3d11.dll"), "D3D11CreateDevice");
if (pD3D11CD)
{
unsigned int nAdapter = 0;
IDXGIAdapter1* pAdapter = 0;
while (pFactory->EnumAdapters1(nAdapter, &pAdapter) != DXGI_ERROR_NOT_FOUND)
{
if (pAdapter)
{
ID3D11Device* pDevice = 0;
D3D_FEATURE_LEVEL levels[] = {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1};
D3D_FEATURE_LEVEL deviceFeatureLevel = D3D_FEATURE_LEVEL_9_1;
HRESULT hr = pD3D11CD(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, levels, sizeof(levels) / sizeof(levels[0]), D3D11_SDK_VERSION, &pDevice, &deviceFeatureLevel, NULL);
if (SUCCEEDED(hr) && pDevice)
{
IDXGIOutput* pOutput = 0;
const bool displaysConnected = SUCCEEDED(pAdapter->EnumOutputs(0, &pOutput)) && pOutput;
SAFE_RELEASE(pOutput);
DXGI_ADAPTER_DESC1 ad;
pAdapter->GetDesc1(&ad);
const Win32SysInspect::DXFeatureLevel fl = GetFeatureLevel(deviceFeatureLevel);
if (featureLevel < fl && displaysConnected)
{
adapterDesc = ad;
featureLevel = fl;
}
}
SAFE_RELEASE(pDevice);
SAFE_RELEASE(pAdapter);
}
++nAdapter;
}
}
}
SAFE_RELEASE(pFactory);
return featureLevel != Win32SysInspect::DXFL_Undefined;
}
bool Win32SysInspect::IsDX11Supported()
{
DXGI_ADAPTER_DESC1 adapterDesc = {};
DXFeatureLevel featureLevel = Win32SysInspect::DXFL_Undefined;
return FindGPU(adapterDesc, featureLevel) && featureLevel >= DXFL_11_0;
}
bool Win32SysInspect::GetGPUInfo(char* pName, size_t bufferSize, unsigned int& vendorID, unsigned int& deviceID, unsigned int& totLocalVidMem, DXFeatureLevel& featureLevel)
{
if (pName && bufferSize)
{
pName[0] = '\0';
}
vendorID = 0;
deviceID = 0;
totLocalVidMem = 0;
featureLevel = Win32SysInspect::DXFL_Undefined;
DXGI_ADAPTER_DESC1 adapterDesc = {};
const bool gpuFound = FindGPU(adapterDesc, featureLevel);
if (gpuFound)
{
vendorID = adapterDesc.VendorId;
deviceID = adapterDesc.DeviceId;
if (pName && bufferSize)
{
sprintf_s(pName, bufferSize, "%s", CryStringUtils::WStrToUTF8(adapterDesc.Description).c_str());
}
totLocalVidMem = adapterDesc.DedicatedVideoMemory;
}
return gpuFound;
}
class CGPURating
{
public:
CGPURating();
~CGPURating();
int GetRating(unsigned int vendorId, unsigned int deviceId) const;
private:
struct SGPUID
{
SGPUID(unsigned int vendorId, unsigned int deviceId)
: vendor(vendorId)
, device(deviceId)
{
}
bool operator < (const SGPUID& rhs) const
{
if (vendor == rhs.vendor)
{
return device < rhs.device;
}
else
{
return vendor < rhs.vendor;
}
}
unsigned int vendor;
unsigned int device;
};
typedef std::map<SGPUID, int> GPURatingMap;
private:
GPURatingMap m_gpuRatingMap;
};
static size_t SafeReadLine(AZ::IO::IArchive* pPak, AZ::IO::HandleType fileHandle, char* buffer, size_t bufferSize)
{
assert(buffer && bufferSize);
memset(buffer, 0, bufferSize);
size_t bytesRead = pPak->FRead(buffer, bufferSize - 1, fileHandle);
if (!bytesRead)
{
return 0;
}
char* currentPosition = buffer;
size_t len = 0;
bool done = false;
int slashRPosition = -1;
do
{
if (*currentPosition != '\r' && *currentPosition != '\n' && len < bufferSize - 1)
{
len++;
currentPosition++;
}
else
{
done = true;
if (*currentPosition == '\r')
{
slashRPosition = len;
}
}
} while (!done);
// null terminate string
buffer[len] = '\0';
//////////////////////////////////////////
//seek back to the end of the string
int seekback = bytesRead - len - 1;
// handle CR/LF for file coming from different platforms
if (slashRPosition > -1 && bytesRead > slashRPosition && buffer[slashRPosition + 1] == '\n')
{
seekback--;
}
pPak->FSeek(fileHandle, -seekback, SEEK_CUR);
///////////////////////////////////////////
return len;
}
#define BUILDPATH_GPURATING(x) "config/gpu/" x
CGPURating::CGPURating()
{
auto pPak(gEnv->pCryPak);
AZ::IO::ArchiveFileIterator h(pPak->FindFirst(BUILDPATH_GPURATING("*.txt")));
if (h)
{
do
{
char filename[128];
azsnprintf(filename, sizeof(filename), BUILDPATH_GPURATING("%.*s"), aznumeric_cast<int>(h.m_filename.size()), h.m_filename.data());
AZ::IO::HandleType fileHandle = pPak->FOpen(filename, "rb");
if (fileHandle != AZ::IO::InvalidHandle)
{
size_t lineNr(0);
while (!pPak->FEof(fileHandle))
{
char line[1024];
line[0] = '\0';
size_t len(SafeReadLine(pPak, fileHandle, line, sizeof(line)));
++lineNr;
if (len > 2 && line[0] != '/' && line[1] != '/')
{
unsigned int vendorId(0), deviceId(0);
int rating(0);
if (_snscanf_s(line, sizeof(line), "%x,%x,%d", &vendorId, &deviceId, &rating) == 3)
{
GPURatingMap::iterator it(m_gpuRatingMap.find(SGPUID(vendorId, deviceId)));
if (it == m_gpuRatingMap.end())
{
m_gpuRatingMap.insert(GPURatingMap::value_type(SGPUID(vendorId, deviceId), rating));
}
else
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING,
"%s line %d contains a multiple defined GPU rating!", filename, lineNr);
}
}
else
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING,
"%s line %d contains incomplete GPU rating!", filename, lineNr);
}
}
}
pPak->FClose(fileHandle);
}
} while (h = pPak->FindNext(h));
pPak->FindClose(h);
}
}
CGPURating::~CGPURating()
{
}
int CGPURating::GetRating(unsigned int vendorId, unsigned int deviceId) const
{
GPURatingMap::const_iterator it(m_gpuRatingMap.find(SGPUID(vendorId, deviceId)));
if (it != m_gpuRatingMap.end())
{
return (*it).second;
}
else
{
return 0;
}
}
int Win32SysInspect::GetGPURating([[maybe_unused]] unsigned int vendorId, [[maybe_unused]] unsigned int deviceId)
{
return 0; // All GPUs unrated as the database is out of date
//CGPURating gpuRatingDb;
//return gpuRatingDb.GetRating(vendorId, deviceId);
}
static int GetFinalSpecValue(int cpuRating, unsigned int totSysMemMB, int gpuRating, unsigned int totVidMemMB, ESystemConfigSpec maxConfigSpec)
{
int sysMemRating = 1;
if (totSysMemMB >= Win32SysInspect::SafeMemoryThreshold(12228))
{
sysMemRating = 3;
}
else if (totSysMemMB >= Win32SysInspect::SafeMemoryThreshold(8192))
{
sysMemRating = 2;
}
cpuRating = sysMemRating < cpuRating ? sysMemRating : cpuRating;
// just a sanity check, GPU should reflect overall GPU perf including memory (higher rated GPUs usually come with enough memory)
if (totVidMemMB < Win32SysInspect::SafeMemoryThreshold(1024))
{
gpuRating = 1;
}
int finalRating = cpuRating < gpuRating ? cpuRating : gpuRating;
return min(finalRating, (int) maxConfigSpec);
}
void CSystem::AutoDetectSpec(const bool detectResolution)
{
CryLogAlways("Running machine spec auto detect (%d bit)...", sizeof(void*) << 3);
char tempBuf[512];
// get OS
SPlatformInfo::EWinVersion winVer(SPlatformInfo::WinUndetected);
bool is64bit(false);
Win32SysInspect::GetOS(winVer, is64bit, tempBuf, sizeof(tempBuf));
CryLogAlways("- %s", tempBuf);
// get system memory
uint64 totSysMem(0);
GetSystemMemory(totSysMem);
CryLogAlways("- System memory");
CryLogAlways("--- %d MB", totSysMem >> 20);
// get CPU name
GetCPUName(tempBuf, sizeof(tempBuf));
TrimExcessiveWhiteSpaces(tempBuf);
CryLogAlways("- %s", tempBuf);
// get number of CPU cores
unsigned int numSysCores(1), numProcCores(1);
Win32SysInspect::GetNumCPUCores(numSysCores, numProcCores);
CryLogAlways("--- Number of available cores: %d (out of %d)", numProcCores, numSysCores);
const int numLogicalProcs = gEnv->pi.numLogicalProcessors;
CryLogAlways("--- Number of logical processors: %d", numLogicalProcs);
// get CPU rating
const int cpuRating = numLogicalProcs >= 8 ? 3 : (numLogicalProcs >= 6 ? 2 : 1);
// get GPU info
unsigned int gpuVendorId(0), gpuDeviceId(0), totVidMem(0);
Win32SysInspect::DXFeatureLevel featureLevel(Win32SysInspect::DXFL_Undefined);
Win32SysInspect::GetGPUInfo(tempBuf, sizeof(tempBuf), gpuVendorId, gpuDeviceId, totVidMem, featureLevel);
CryLogAlways("- %s (vendor = 0x%.4x, device = 0x%.4x)", tempBuf, gpuVendorId, gpuDeviceId);
CryLogAlways("--- Dedicated video memory: %d MB", totVidMem >> 20);
CryLogAlways("--- Feature level: %s", GetFeatureLevelAsString(featureLevel));
// get GPU rating
const int gpuRating = (totVidMem >> 20) >= Win32SysInspect::SafeMemoryThreshold(4096) ? 3 : ((totVidMem >> 20) >= Win32SysInspect::SafeMemoryThreshold(2048) ? 2 : 1);
// get final rating
int finalSpecValue(GetFinalSpecValue(cpuRating, totSysMem >> 20, gpuRating, totVidMem >> 20, CONFIG_VERYHIGH_SPEC));
CryLogAlways("- Final rating: Machine class %d", finalSpecValue);
m_sys_GraphicsQuality->Set(finalSpecValue);
if (detectResolution)
{
if ((m_rWidth->GetFlags() & VF_WASINCONFIG) == 0)
{
m_rWidth->Set(GetSystemMetrics(SM_CXFULLSCREEN));
}
if ((m_rHeight->GetFlags() & VF_WASINCONFIG) == 0)
{
m_rHeight->Set(GetSystemMetrics(SM_CYFULLSCREEN));
}
if ((m_rFullscreen->GetFlags() & VF_WASINCONFIG) == 0)
{
m_rFullscreen->Set(1);
}
}
}
#else
#include "System.h"
void CSystem::AutoDetectSpec(const bool detectResolution)
{
AZ_UNUSED(detectResolution);
}
#endif