diff --git a/Source/Engine/Graphics/GPUAdapter.h b/Source/Engine/Graphics/GPUAdapter.h index af235a085..f845d7e7f 100644 --- a/Source/Engine/Graphics/GPUAdapter.h +++ b/Source/Engine/Graphics/GPUAdapter.h @@ -2,6 +2,7 @@ #pragma once +#include "Engine/Core/Types/Version.h" #include "Engine/Scripting/ScriptingObject.h" // GPU vendors IDs @@ -56,6 +57,11 @@ public: /// API_PROPERTY() virtual String GetDescription() const = 0; + /// + /// Gets the GPU driver version. + /// + API_PROPERTY() virtual Version GetDriverVersion() const = 0; + public: // Returns true if adapter's vendor is AMD. API_PROPERTY() FORCE_INLINE bool IsAMD() const diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index b4fbefb7a..c189b6e50 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -182,12 +182,12 @@ bool GraphicsService::Init() return true; } GPUDevice::Instance = device; - LOG(Info, - "Graphics Device created! Adapter: \'{0}\', Renderer: {1}, Shader Profile: {2}, Feature Level: {3}", - device->GetAdapter()->GetDescription(), + LOG(Info, "GPU Device created: {}", device->GetAdapter()->GetDescription()); + LOG(Info, "Renderer: {}, Shader Profile: {}, Feature Level: {}, Driver: {}", ::ToString(device->GetRendererType()), ::ToString(device->GetShaderProfile()), - ::ToString(device->GetFeatureLevel()) + ::ToString(device->GetFeatureLevel()), + device->GetAdapter()->GetDriverVersion().ToString() ); // Initialize diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index ac11989d5..f6dff7b44 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -296,8 +296,6 @@ GPUDevice* GPUDeviceDX11::Create() } } } - - // Validate adapter if (!selectedAdapter.IsValid()) { LOG(Error, "Failed to choose valid DirectX adapter!"); @@ -427,7 +425,7 @@ bool GPUDeviceDX11::Init() // Create DirectX device D3D_FEATURE_LEVEL createdFeatureLevel = static_cast(0); - auto targetFeatureLevel = GetD3DFeatureLevel(); + D3D_FEATURE_LEVEL targetFeatureLevel = _adapter->MaxFeatureLevel; VALIDATE_DIRECTX_CALL(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, &targetFeatureLevel, 1, D3D11_SDK_VERSION, &_device, &createdFeatureLevel, &_imContext)); ASSERT(_device); ASSERT(_imContext); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 65e3f6d83..e646dd650 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -198,17 +198,14 @@ GPUDevice* GPUDeviceDX12::Create() } } } - - // Validate adapter if (!selectedAdapter.IsValid()) { LOG(Error, "Failed to choose valid DirectX adapter!"); return nullptr; } - - // Check if selected adapter does not support DirectX 12 - if (!selectedAdapter.IsSupportingDX12()) + if (selectedAdapter.MaxFeatureLevel < D3D_FEATURE_LEVEL_12_0) { + LOG(Error, "Failed to choose valid DirectX adapter!"); return nullptr; } #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/GPUAdapterDX.h b/Source/Engine/GraphicsDevice/DirectX/GPUAdapterDX.h index 881d40a17..5a5ef1d69 100644 --- a/Source/Engine/GraphicsDevice/DirectX/GPUAdapterDX.h +++ b/Source/Engine/GraphicsDevice/DirectX/GPUAdapterDX.h @@ -14,25 +14,18 @@ class GPUAdapterDX : public GPUAdapter { public: - int32 Index = INVALID_INDEX; D3D_FEATURE_LEVEL MaxFeatureLevel; DXGI_ADAPTER_DESC Description; + Version DriverVersion = Version(0, 0); public: + void GetDriverVersion(); - // Returns true if adapter is supporting DirectX 12. - FORCE_INLINE bool IsSupportingDX12() const - { -#if GRAPHICS_API_DIRECTX12 - return MaxFeatureLevel >= D3D_FEATURE_LEVEL_12_0; -#else - return false; -#endif - } +private: + void SetDriverVersion(Version& ver); public: - // [GPUAdapter] bool IsValid() const override { @@ -50,6 +43,10 @@ public: { return Description.Description; } + Version GetDriverVersion() const override + { + return DriverVersion; + } }; #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/GPUDeviceDX.h b/Source/Engine/GraphicsDevice/DirectX/GPUDeviceDX.h index 61b61847e..7fa9087ab 100644 --- a/Source/Engine/GraphicsDevice/DirectX/GPUDeviceDX.h +++ b/Source/Engine/GraphicsDevice/DirectX/GPUDeviceDX.h @@ -36,17 +36,10 @@ protected: : GPUDevice(type, profile) , _adapter(adapter) { + adapter->GetDriverVersion(); } public: - /// - /// Gets DirectX device feature level. - /// - FORCE_INLINE D3D_FEATURE_LEVEL GetD3DFeatureLevel() const - { - return _adapter->MaxFeatureLevel; - } - /// /// The video outputs. /// diff --git a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp index 39a975b74..9832e4a12 100644 --- a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp @@ -3,11 +3,24 @@ #if GRAPHICS_API_DIRECTX11 || GRAPHICS_API_DIRECTX12 #include "RenderToolsDX.h" +#include "GPUAdapterDX.h" #include "GPUDeviceDX.h" +#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/StringBuilder.h" #include "Engine/Graphics/GPUDevice.h" +#include "IncludeDirectXHeaders.h" #include +#define GPU_DRIVER_DETECTION_WIN32_REGISTRY (PLATFORM_WINDOWS) +#define GPU_DRIVER_DETECTION_WIN32_SETUPAPI (PLATFORM_WINDOWS) + +#if GPU_DRIVER_DETECTION_WIN32_SETUPAPI +#define _SETUPAPI_VER WINVER +typedef void* LPCDLGTEMPLATE; +#include +#pragma comment(lib, "SetupAPI.lib") +#endif + namespace Windows { typedef struct _devicemodeW @@ -304,7 +317,7 @@ void FormatD3DErrorString(HRESULT errorCode, StringBuilder& sb, HRESULT& removed default: sb.AppendFormat(TEXT("0x{0:x}"), static_cast(errorCode)); - break; + break; } #undef D3DERR @@ -439,6 +452,140 @@ LPCSTR RenderToolsDX::GetVertexInputSemantic(VertexElement::Types type, UINT& se return ""; } } + +void GPUAdapterDX::GetDriverVersion() +{ +#if GPU_DRIVER_DETECTION_WIN32_REGISTRY + { + // Reference: https://github.com/GameTechDev/gpudetect/blob/master/GPUDetect.cpp + + // Fetch registry data + HKEY dxKeyHandle = nullptr; + DWORD numOfAdapters = 0; + LSTATUS returnCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\DirectX"), 0, KEY_READ, &dxKeyHandle); + if (returnCode == S_OK) + { + // Find all sub keys + DWORD subKeyMaxLength = 0; + returnCode = RegQueryInfoKeyW(dxKeyHandle, 0, 0, 0, &numOfAdapters, &subKeyMaxLength, 0, 0, 0, 0, 0, 0); + if (returnCode == S_OK && subKeyMaxLength < 100) + { + subKeyMaxLength += 1; + uint64_t driverVersionRaw = 0; + TCHAR subKeyName[100]; + for (DWORD i = 0; i < numOfAdapters; i++) + { + DWORD subKeyLength = subKeyMaxLength; + returnCode = RegEnumKeyExW(dxKeyHandle, i, subKeyName, &subKeyLength, 0, 0, 0, 0); + if (returnCode == S_OK) + { + LUID adapterLUID = {}; + DWORD qwordSize = sizeof(uint64_t); + returnCode = RegGetValueW(dxKeyHandle, subKeyName, TEXT("AdapterLuid"), RRF_RT_QWORD, 0, &adapterLUID, &qwordSize); + if (returnCode == S_OK && adapterLUID.HighPart == Description.AdapterLuid.HighPart && adapterLUID.LowPart == Description.AdapterLuid.LowPart) + { + // Get driver version + returnCode = RegGetValueW(dxKeyHandle, subKeyName, TEXT("DriverVersion"), RRF_RT_QWORD, 0, &driverVersionRaw, &qwordSize); + if (returnCode == S_OK) + { + Version driverVersion( + (int32)((driverVersionRaw & 0xFFFF000000000000) >> 16 * 3), + (int32)((driverVersionRaw & 0x0000FFFF00000000) >> 16 * 2), + (int32)((driverVersionRaw & 0x00000000FFFF0000) >> 16 * 1), + (int32)((driverVersionRaw & 0x000000000000FFFF))); + SetDriverVersion(driverVersion); + } + break; + } + } + } + } + RegCloseKey(dxKeyHandle); + } + + if (DriverVersion != Version(0, 0)) + return; + } +#endif + +#if GPU_DRIVER_DETECTION_WIN32_SETUPAPI + { + // Reference: https://gist.github.com/LxLasso/eccee4d71c2e49492f2cbf01a966fa73 + + // Copied from devguid.h and devpkey.h +#define MAKE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#define MAKE_DEVPROPKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) const DEVPROPKEY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } + MAKE_GUID(GUID_DEVCLASS_DISPLAY, 0x4d36e968L, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18); + MAKE_DEVPROPKEY(DEVPKEY_Device_DriverDate, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 2); + MAKE_DEVPROPKEY(DEVPKEY_Device_DriverVersion, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 3); +#undef MAKE_DEVPROPKEY +#undef MAKE_GUID + + HDEVINFO deviceInfoList = SetupDiGetClassDevs(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT); + if (deviceInfoList != INVALID_HANDLE_VALUE) + { + SP_DEVINFO_DATA deviceInfo; + ZeroMemory(&deviceInfo, sizeof(deviceInfo)); + deviceInfo.cbSize = sizeof(SP_DEVINFO_DATA); + + wchar_t searchBuffer[128]; + swprintf(searchBuffer, sizeof(searchBuffer) / 2, L"PCI\\VEN_%04X&DEV_%04X&SUBSYS_%04X", Description.VendorId, Description.DeviceId, Description.SubSysId); + size_t searchBufferLen = wcslen(searchBuffer); + + DWORD deviceIndex = 0; + DEVPROPTYPE propertyType; + wchar_t buffer[300]; + while (SetupDiEnumDeviceInfo(deviceInfoList, deviceIndex, &deviceInfo)) + { + DWORD deviceIdSize; + if (SetupDiGetDeviceInstanceId(deviceInfoList, &deviceInfo, buffer, sizeof(buffer), &deviceIdSize) && + wcsncmp(buffer, searchBuffer, searchBufferLen) == 0) + { + // Get driver version + if (SetupDiGetDeviceProperty(deviceInfoList, &deviceInfo, &DEVPKEY_Device_DriverVersion, &propertyType, (PBYTE)buffer, sizeof(buffer), NULL, 0) && + propertyType == DEVPROP_TYPE_STRING) + { + //ParseDriverVersionBuffer(buffer); + Version driverVersion; + String bufferStr(buffer); + if (!Version::Parse(bufferStr, &driverVersion)) + SetDriverVersion(driverVersion); + } + +#if 0 + // Get driver date + DEVPROPTYPE propertyType; + if (SetupDiGetDeviceProperty(deviceInfoList, &deviceInfo, &DEVPKEY_Device_DriverDate, &propertyType, (PBYTE)buffer, sizeof(FILETIME), NULL, 0) && + propertyType == DEVPROP_TYPE_FILETIME) + { + SYSTEMTIME deviceDriverSystemTime; + FileTimeToSystemTime((FILETIME*)buffer, &deviceDriverSystemTime); + DriverDate = DateTime(deviceDriverSystemTime.wYear, deviceDriverSystemTime.wMonth, deviceDriverSystemTime.wDay, deviceDriverSystemTime.wHour, deviceDriverSystemTime.wMinute, deviceDriverSystemTime.wSecond, deviceDriverSystemTime.wMilliseconds); + } +#endif + } + deviceIndex++; + } + + SetupDiDestroyDeviceInfoList(deviceInfoList); + } + + if (DriverVersion != Version(0, 0)) + return; + } +#endif +} + +void GPUAdapterDX::SetDriverVersion(Version& ver) +{ + if (IsNVIDIA() && ver.Build() > 0 && ver.Revision() > 99) + { + // Convert NVIDIA version from 32.0.15.7247 into 572.47 + ver = Version((ver.Build() % 10) * 100 + ver.Revision() / 100, ver.Revision() % 100); + } + DriverVersion = ver; +} + void GPUDeviceDX::UpdateOutputs(IDXGIAdapter* adapter) { #if PLATFORM_WINDOWS diff --git a/Source/Engine/GraphicsDevice/Null/GPUAdapterNull.h b/Source/Engine/GraphicsDevice/Null/GPUAdapterNull.h index 28cbac304..8f2fe49e8 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUAdapterNull.h +++ b/Source/Engine/GraphicsDevice/Null/GPUAdapterNull.h @@ -29,6 +29,10 @@ public: { return TEXT("Null"); } + Version GetDriverVersion() const override + { + return Version(0, 0); + } }; #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h index e109293a6..66b9256fd 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h @@ -70,6 +70,27 @@ public: { return Description; } + Version GetDriverVersion() const override + { + Version version(VK_VERSION_MAJOR(GpuProps.driverVersion), VK_VERSION_MINOR(GpuProps.driverVersion), VK_VERSION_PATCH(GpuProps.driverVersion)); + if (IsNVIDIA()) + { + union NvidiaDriverVersion + { + struct + { + uint32 Tertiary : 6; + uint32 Secondary : 8; + uint32 Minor : 8; + uint32 Major : 10; + }; + uint32 Packed; + } NvidiaVersion; + NvidiaVersion.Packed = GpuProps.driverVersion; + version = Version(NvidiaVersion.Major, NvidiaVersion.Minor); + } + return version; + } }; #endif