Files
FlaxEngine/Source/Engine/Platform/Win32/Win32Platform.cpp
Jean-Baptiste Perrier e9f72dbbbf Adding ACLineStatus enum.
2021-01-02 13:15:15 +01:00

517 lines
13 KiB
C++

// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#if PLATFORM_WIN32
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/MemoryStats.h"
#include "Engine/Platform/CPUInfo.h"
#include "Engine/Platform/BatteryInfo.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Math/Math.h"
#include "IncludeWindowsHeaders.h"
#include "Engine/Core/Collections/HashFunctions.h"
#include <Psapi.h>
#include <WinSock2.h>
#include <IPHlpApi.h>
#include <oleauto.h>
#include <WinBase.h>
#pragma comment(lib, "Iphlpapi.lib")
namespace
{
Guid DeviceId;
CPUInfo CpuInfo;
uint64 ClockFrequency;
double CyclesToSeconds;
}
// Helper function to count set bits in the processor mask
DWORD CountSetBits(ULONG_PTR bitMask)
{
DWORD LSHIFT = sizeof(ULONG_PTR) * 8 - 1;
DWORD bitSetCount = 0;
ULONG_PTR bitTest = static_cast<ULONG_PTR>(1) << LSHIFT;
DWORD i;
for (i = 0; i <= LSHIFT; ++i)
{
bitSetCount += ((bitMask & bitTest) ? 1 : 0);
bitTest /= 2;
}
return bitSetCount;
}
bool Win32Platform::Init()
{
if (PlatformBase::Init())
return true;
// Init timing
LARGE_INTEGER frequency;
const auto freqResult = QueryPerformanceFrequency(&frequency);
ASSERT(freqResult && frequency.QuadPart > 0);
ClockFrequency = frequency.QuadPart;
CyclesToSeconds = 1.0 / static_cast<double>(frequency.QuadPart);
BOOL done = FALSE;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = nullptr;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr;
DWORD returnLength = 0;
DWORD logicalProcessorCount = 0;
DWORD processorCoreCount = 0;
DWORD processorL1CacheSize = 0;
DWORD processorL2CacheSize = 0;
DWORD processorL3CacheSize = 0;
DWORD processorPackageCount = 0;
DWORD byteOffset = 0;
PCACHE_DESCRIPTOR cache;
while (!done)
{
DWORD rc = GetLogicalProcessorInformation(buffer, &returnLength);
if (FALSE == rc)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (buffer)
{
free(buffer);
}
buffer = static_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(malloc(returnLength));
if (buffer == nullptr)
{
return true;
}
}
else
{
return true;
}
}
else
{
done = TRUE;
}
}
ptr = buffer;
while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength)
{
switch (ptr->Relationship)
{
case RelationProcessorCore:
processorCoreCount++;
// A hyper threaded core supplies more than one logical processor
logicalProcessorCount += CountSetBits(ptr->ProcessorMask);
break;
case RelationCache:
// Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache.
cache = &ptr->Cache;
if (cache->Level == 1)
{
processorL1CacheSize += cache->Size;
}
else if (cache->Level == 2)
{
processorL2CacheSize += cache->Size;
}
else if (cache->Level == 3)
{
processorL3CacheSize += cache->Size;
}
break;
case RelationProcessorPackage:
// Logical processors share a physical package
processorPackageCount++;
break;
}
byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
ptr++;
}
free(buffer);
// Set info about the CPU
CpuInfo.ProcessorPackageCount = processorPackageCount;
CpuInfo.ProcessorCoreCount = processorCoreCount;
CpuInfo.LogicalProcessorCount = logicalProcessorCount;
CpuInfo.L1CacheSize = processorL1CacheSize;
CpuInfo.L2CacheSize = processorL2CacheSize;
CpuInfo.L3CacheSize = processorL3CacheSize;
// Get page size
SYSTEM_INFO siSysInfo;
GetSystemInfo(&siSysInfo);
CpuInfo.PageSize = siSysInfo.dwPageSize;
// Get clock speed
CpuInfo.ClockSpeed = GetClockFrequency();
// Get cache line size
{
int args[4];
__cpuid(args, 0x80000006);
CpuInfo.CacheLineSize = args[2] & 0xFF;
ASSERT(CpuInfo.CacheLineSize && Math::IsPowerOfTwo(CpuInfo.CacheLineSize));
}
// Setup unique device ID
{
DeviceId = Guid::Empty;
// A - Computer Name and User Name
uint32 hash = GetHash(Platform::GetComputerName());
CombineHash(hash, GetHash(Platform::GetUserName()));
DeviceId.A = hash;
// B - MAC address
DeviceId.B = 0;
IP_ADAPTER_ADDRESSES pAddresses[16];
DWORD dwBufLen = sizeof(pAddresses);
#if PLATFORM_UWP
DWORD dwStatus = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &dwBufLen);
PIP_ADAPTER_ADDRESSES pAdapterInfo = dwStatus == ERROR_SUCCESS ? pAddresses : nullptr;
while (pAdapterInfo)
{
hash = pAdapterInfo->PhysicalAddress[0];
for (UINT i = 1; i < pAdapterInfo->PhysicalAddressLength; i++)
CombineHash(hash, pAdapterInfo->PhysicalAddress[i]);
DeviceId.B = hash;
pAdapterInfo = pAdapterInfo->Next;
}
#else
const DWORD dwStatus = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &dwBufLen);
PIP_ADAPTER_ADDRESSES pAdapterInfo = dwStatus == ERROR_SUCCESS ? pAddresses : nullptr;
while (pAdapterInfo)
{
hash = pAdapterInfo->PhysicalAddress[0];
for (UINT i = 1; i < pAdapterInfo->PhysicalAddressLength; i++)
CombineHash(hash, pAdapterInfo->PhysicalAddress[i]);
DeviceId.B = hash;
pAdapterInfo = pAdapterInfo->Next;
}
#endif
// C - memory
DeviceId.C = (uint32)Platform::GetMemoryStats().TotalPhysicalMemory;
// D - cpuid
auto cpuInfo = Platform::GetCPUInfo();
DeviceId.D = (uint32)cpuInfo.ClockSpeed * cpuInfo.LogicalProcessorCount * cpuInfo.ProcessorCoreCount * cpuInfo.CacheLineSize;
}
return false;
}
void Win32Platform::MemoryBarrier()
{
// NOTE: _ReadWriteBarrier and friends only prevent the
// compiler from reordering loads and stores. To prevent
// the CPU from doing the same, we have to use the
// MemoryBarrier macro which expands to e.g. a serializing
// XCHG instruction on x86. Also note that the MemoryBarrier
// macro does *not* imply _ReadWriteBarrier, so that call
// cannot be eliminated.
_ReadWriteBarrier();
// MemoryBarrier macro (we use undef to hide some symbols from Windows headers)
#if PLATFORM_64BITS
#ifdef _AMD64_
__faststorefence();
#elif defined(_IA64_)
__mf();
#else
#error "Invalid platform."
#endif
#else
LONG barrier;
__asm {
xchg barrier, eax
}
#endif
}
int64 Win32Platform::InterlockedExchange(int64 volatile* dst, int64 exchange)
{
return InterlockedExchange64(dst, exchange);
}
int32 Win32Platform::InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand)
{
static_assert(sizeof(int32) == sizeof(LONG), "Invalid LONG size.");
return _InterlockedCompareExchange((LONG volatile*)dst, exchange, comperand);
}
int64 Win32Platform::InterlockedCompareExchange(int64 volatile* dst, int64 exchange, int64 comperand)
{
return InterlockedCompareExchange64(dst, exchange, comperand);
}
int64 Win32Platform::InterlockedIncrement(int64 volatile* dst)
{
return InterlockedIncrement64(dst);
}
int64 Win32Platform::InterlockedDecrement(int64 volatile* dst)
{
return InterlockedDecrement64(dst);
}
int64 Win32Platform::InterlockedAdd(int64 volatile* dst, int64 value)
{
return InterlockedExchangeAdd64(dst, value);
}
int32 Win32Platform::AtomicRead(int32 volatile* dst)
{
static_assert(sizeof(int32) == sizeof(LONG), "Invalid LONG size.");
return _InterlockedCompareExchange((LONG volatile*)dst, 0, 0);
}
int64 Win32Platform::AtomicRead(int64 volatile* dst)
{
return InterlockedCompareExchange64(dst, 0, 0);
}
void Win32Platform::AtomicStore(int32 volatile* dst, int32 value)
{
static_assert(sizeof(int32) == sizeof(LONG), "Invalid LONG size.");
_InterlockedExchange((LONG volatile*)dst, value);
}
void Win32Platform::AtomicStore(int64 volatile* dst, int64 value)
{
InterlockedExchange64(dst, value);
}
bool Win32Platform::Is64BitPlatform()
{
#ifdef PLATFORM_64BITS
return true;
#else
BOOL result;
IsWow64Process(GetCurrentProcess(), &result);
return result == TRUE;
#endif
}
BatteryInfo Win32Platform::GetBatteryInfo()
{
BatteryInfo info;
SYSTEM_POWER_STATUS status;
GetSystemPowerStatus(&status);
info.ACLineStatus = (ACLineStatus)status.ACLineStatus;
info.BatteryLifePercent = status.BatteryLifePercent;
info.BatteryLifeTime = status.BatteryLifeTime;
return info;
}
CPUInfo Win32Platform::GetCPUInfo()
{
return CpuInfo;
}
int32 Win32Platform::GetCacheLineSize()
{
return CpuInfo.CacheLineSize;
}
MemoryStats Win32Platform::GetMemoryStats()
{
// Get memory stats
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
GlobalMemoryStatusEx(&statex);
// Fill result data
MemoryStats result;
result.TotalPhysicalMemory = statex.ullTotalPhys;
result.UsedPhysicalMemory = statex.ullTotalPhys - statex.ullAvailPhys;
result.TotalVirtualMemory = statex.ullTotalVirtual;
result.UsedVirtualMemory = statex.ullTotalVirtual - statex.ullAvailVirtual;
return result;
}
ProcessMemoryStats Win32Platform::GetProcessMemoryStats()
{
// Get memory stats
PROCESS_MEMORY_COUNTERS_EX countersEx;
countersEx.cb = sizeof(countersEx);
GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&countersEx, sizeof(countersEx));
// Fill result data
ProcessMemoryStats result;
result.UsedPhysicalMemory = countersEx.WorkingSetSize;
result.UsedVirtualMemory = countersEx.PrivateUsage;
return result;
}
uint64 Win32Platform::GetCurrentProcessId()
{
return ::GetCurrentProcessId();
}
uint64 Win32Platform::GetCurrentThreadID()
{
return ::GetCurrentThreadId();
}
void Win32Platform::SetThreadPriority(ThreadPriority priority)
{
int32 winPriority;
switch (priority)
{
case ThreadPriority::Lowest:
winPriority = THREAD_PRIORITY_LOWEST;
break;
case ThreadPriority::BelowNormal:
winPriority = THREAD_PRIORITY_BELOW_NORMAL;
break;
case ThreadPriority::Normal:
winPriority = THREAD_PRIORITY_NORMAL;
break;
case ThreadPriority::AboveNormal:
winPriority = THREAD_PRIORITY_ABOVE_NORMAL;
break;
case ThreadPriority::Highest:
winPriority = THREAD_PRIORITY_HIGHEST;
break;
default:
winPriority = THREAD_PRIORITY_NORMAL;
break;
}
::SetThreadPriority(::GetCurrentThread(), winPriority);
}
void Win32Platform::SetThreadAffinityMask(uint64 affinityMask)
{
::SetThreadAffinityMask(::GetCurrentThread(), (DWORD_PTR)affinityMask);
}
void Win32Platform::Sleep(int32 milliseconds)
{
::Sleep(static_cast<DWORD>(milliseconds));
}
double Win32Platform::GetTimeSeconds()
{
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return double(counter.QuadPart) * CyclesToSeconds;
}
uint64 Win32Platform::GetTimeCycles()
{
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart;
}
uint64 Win32Platform::GetClockFrequency()
{
return ClockFrequency;
}
void Win32Platform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
{
// Get current local time
SYSTEMTIME st;
::GetLocalTime(&st);
// Extract time
year = st.wYear;
month = st.wMonth;
dayOfWeek = st.wDayOfWeek;
day = st.wDay;
hour = st.wHour;
minute = st.wMinute;
second = st.wSecond;
millisecond = st.wMilliseconds;
}
void Win32Platform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
{
// Get current system time
SYSTEMTIME st;
::GetSystemTime(&st);
// Extract time
year = st.wYear;
month = st.wMonth;
dayOfWeek = st.wDayOfWeek;
day = st.wDay;
hour = st.wHour;
minute = st.wMinute;
second = st.wSecond;
millisecond = st.wMilliseconds;
}
void Win32Platform::CreateGuid(Guid& result)
{
CoCreateGuid(reinterpret_cast<GUID*>(&result));
}
String Win32Platform::GetMainDirectory()
{
Char buffer[MAX_PATH];
GetModuleFileNameW(nullptr, buffer, MAX_PATH);
const String str(buffer);
int32 pos = str.FindLast(TEXT('\\'));
if (pos != -1 && ++pos < str.Length())
return str.Left(pos);
return str;
}
String Win32Platform::GetExecutableFilePath()
{
Char buffer[MAX_PATH];
GetModuleFileNameW(nullptr, buffer, MAX_PATH);
return String(buffer);
}
Guid Win32Platform::GetUniqueDeviceId()
{
return DeviceId;
}
String Win32Platform::GetWorkingDirectory()
{
Char buffer[MAX_PATH];
GetCurrentDirectoryW(MAX_PATH, buffer);
return String(buffer);
}
bool Win32Platform::SetWorkingDirectory(const String& path)
{
return !SetCurrentDirectoryW(*path);
}
void Win32Platform::FreeLibrary(void* handle)
{
::FreeLibrary((HMODULE)handle);
}
void* Win32Platform::GetProcAddress(void* handle, const char* symbol)
{
return (void*)::GetProcAddress((HMODULE)handle, symbol);
}
#endif