504 lines
13 KiB
C++
504 lines
13 KiB
C++
// Copyright (c) 2012-2024 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/Core/Types/Guid.h"
|
|
#include "Engine/Core/Types/String.h"
|
|
#include "Engine/Core/Math/Math.h"
|
|
#include "Engine/Core/Collections/HashFunctions.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "IncludeWindowsHeaders.h"
|
|
#include <Psapi.h>
|
|
#include <WinSock2.h>
|
|
#include <IPHlpApi.h>
|
|
#include <oleauto.h>
|
|
#include <WinBase.h>
|
|
#include <xmmintrin.h>
|
|
#include <intrin.h>
|
|
#pragma comment(lib, "Iphlpapi.lib")
|
|
|
|
static_assert(sizeof(int32) == sizeof(long), "Invalid long size for Interlocked and Atomic operations in Win32Platform.");
|
|
|
|
namespace
|
|
{
|
|
Guid DeviceId;
|
|
CPUInfo CpuInfo;
|
|
uint64 ClockFrequency;
|
|
double CyclesToSeconds;
|
|
WSAData WsaData;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
static String GetLastErrorMessage()
|
|
{
|
|
wchar_t* s = nullptr;
|
|
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
nullptr, WSAGetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
reinterpret_cast<LPWSTR>(&s), 0, nullptr);
|
|
String str(s);
|
|
LocalFree(s);
|
|
return str;
|
|
}
|
|
|
|
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);
|
|
|
|
// Count CPUs
|
|
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 (rc == FALSE)
|
|
{
|
|
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++;
|
|
logicalProcessorCount += CountSetBits(ptr->ProcessorMask);
|
|
break;
|
|
case RelationCache:
|
|
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:
|
|
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;
|
|
SYSTEM_INFO siSysInfo;
|
|
GetSystemInfo(&siSysInfo);
|
|
CpuInfo.PageSize = siSysInfo.dwPageSize;
|
|
CpuInfo.ClockSpeed = ClockFrequency;
|
|
{
|
|
#ifdef _M_ARM64
|
|
CpuInfo.CacheLineSize = 128;
|
|
#else
|
|
int args[4];
|
|
__cpuid(args, 0x80000006);
|
|
CpuInfo.CacheLineSize = args[2] & 0xFF;
|
|
ASSERT(CpuInfo.CacheLineSize && Math::IsPowerOfTwo(CpuInfo.CacheLineSize));
|
|
#endif
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Init networking
|
|
if (WSAStartup(MAKEWORD(2, 0), &WsaData) != 0)
|
|
LOG(Error, "Unable to initializes native network! Error : {0}", GetLastErrorMessage());
|
|
|
|
return false;
|
|
}
|
|
|
|
void Win32Platform::Exit()
|
|
{
|
|
WSACleanup();
|
|
}
|
|
|
|
void Win32Platform::MemoryBarrier()
|
|
{
|
|
_ReadWriteBarrier();
|
|
#if PLATFORM_64BITS
|
|
#if defined(_AMD64_)
|
|
__faststorefence();
|
|
#elif defined(_IA64_)
|
|
__mf();
|
|
#elif defined(_ARM64_)
|
|
__dmb(_ARM64_BARRIER_ISH);
|
|
#else
|
|
#error "Invalid platform."
|
|
#endif
|
|
#else
|
|
LONG barrier;
|
|
__asm {
|
|
xchg barrier, eax
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Win32Platform::Prefetch(void const* ptr)
|
|
{
|
|
#if _M_ARM64
|
|
__prefetch((char const*)ptr);
|
|
#else
|
|
_mm_prefetch((char const*)ptr, _MM_HINT_T0);
|
|
#endif
|
|
}
|
|
|
|
void* Win32Platform::Allocate(uint64 size, uint64 alignment)
|
|
{
|
|
void* ptr = _aligned_malloc((size_t)size, (size_t)alignment);
|
|
#if COMPILE_WITH_PROFILER
|
|
OnMemoryAlloc(ptr, size);
|
|
#endif
|
|
return ptr;
|
|
}
|
|
|
|
void Win32Platform::Free(void* ptr)
|
|
{
|
|
#if COMPILE_WITH_PROFILER
|
|
OnMemoryFree(ptr);
|
|
#endif
|
|
_aligned_free(ptr);
|
|
}
|
|
|
|
void* Win32Platform::AllocatePages(uint64 numPages, uint64 pageSize)
|
|
{
|
|
const uint64 numBytes = numPages * pageSize;
|
|
#if PLATFORM_UWP
|
|
return VirtualAllocFromApp(nullptr, (SIZE_T)numBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
#else
|
|
return VirtualAlloc(nullptr, (SIZE_T)numBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
#endif
|
|
}
|
|
|
|
void Win32Platform::FreePages(void* ptr)
|
|
{
|
|
VirtualFree(ptr, 0, MEM_RELEASE);
|
|
}
|
|
|
|
bool Win32Platform::Is64BitPlatform()
|
|
{
|
|
#ifdef PLATFORM_64BITS
|
|
return true;
|
|
#else
|
|
BOOL result;
|
|
IsWow64Process(GetCurrentProcess(), &result);
|
|
return result == TRUE;
|
|
#endif
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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)
|
|
{
|
|
static thread_local HANDLE timer = NULL;
|
|
if (timer == NULL)
|
|
{
|
|
// Attempt to create high-resolution timer for each thread (Windows 10 build 17134 or later)
|
|
timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
|
if (timer == NULL) // fallback for older versions of Windows
|
|
timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
|
}
|
|
|
|
// Negative value is relative to current time, minimum waitable time is 10 microseconds
|
|
LARGE_INTEGER dueTime;
|
|
dueTime.QuadPart = -int64_t(milliseconds) * 10000;
|
|
|
|
SetWaitableTimerEx(timer, &dueTime, 0, NULL, NULL, NULL, 0);
|
|
WaitForSingleObject(timer, INFINITE);
|
|
}
|
|
|
|
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
|