Files
2025-05-18 13:04:45 +08:00

318 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Windows/D3D/SlateD3DFindBestDevice.h"
#include "Misc/CommandLine.h"
#include "StandaloneRendererPlatformHeaders.h"
#include "StandaloneRendererLog.h"
#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/D3D/SlateD3DRenderer.h"
#include "Windows/WindowsPlatformCrashContext.h"
#include <delayimp.h>
THIRD_PARTY_INCLUDES_START
//#include <d3d11_2.h>
#include <dxgi1_6.h>
#include <dxgidebug.h>
THIRD_PARTY_INCLUDES_END
#include "Microsoft/HideMicrosoftPlatformTypes.h"
namespace UE::StandaloneRenderer::D3D
{
namespace Private
{
bool IsDelayLoadException(PEXCEPTION_POINTERS ExceptionPointers)
{
switch (ExceptionPointers->ExceptionRecord->ExceptionCode)
{
case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
return EXCEPTION_EXECUTE_HANDLER;
default:
return EXCEPTION_CONTINUE_SEARCH;
}
}
} //namespace Private
FFindBestDevice::FFindBestDevice()
{
ChosenFeatureLevel = D3D_FEATURE_LEVEL_1_0_CORE;
GpuPreference = DXGI_GPU_PREFERENCE_UNSPECIFIED;
LoadSettings();
SelectAdapter();
}
FFindBestDevice::~FFindBestDevice()
{}
bool FFindBestDevice::IsValid() const
{
return ChosenAdapter.IsValid() && ChosenFeatureLevel != D3D_FEATURE_LEVEL_1_0_CORE;
}
bool FFindBestDevice::CreateDevice(ID3D11Device** Device, ID3D11DeviceContext** DeviceContext)
{
if (!IsValid())
{
return false;
}
// Init D3D
D3D_DRIVER_TYPE DriverType = D3D_DRIVER_TYPE_HARDWARE;
uint32 DeviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED;
if (bDebug)
{
DeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
}
D3D_FEATURE_LEVEL CreatedFeatureLevel = D3D_FEATURE_LEVEL_1_0_CORE;
HRESULT Hr = D3D11CreateDevice(ChosenAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, DeviceFlags, &ChosenFeatureLevel, 1, D3D11_SDK_VERSION, Device, &CreatedFeatureLevel, DeviceContext);
if (FAILED(Hr))
{
LogSlateD3DRendererFailure(TEXT("FSlateD3DRenderer::CreateDevice() - D3D11CreateDevice"), Hr);
}
check(CreatedFeatureLevel == ChosenFeatureLevel);
return SUCCEEDED(Hr);
}
const TCHAR* FFindBestDevice::GetFeatureLevelString(D3D_FEATURE_LEVEL FeatureLevel)
{
switch (FeatureLevel)
{
case D3D_FEATURE_LEVEL_9_1: return TEXT("9_1");
case D3D_FEATURE_LEVEL_9_2: return TEXT("9_2");
case D3D_FEATURE_LEVEL_9_3: return TEXT("9_3");
case D3D_FEATURE_LEVEL_10_0: return TEXT("10_0");
case D3D_FEATURE_LEVEL_10_1: return TEXT("10_1");
case D3D_FEATURE_LEVEL_11_0: return TEXT("11_0");
case D3D_FEATURE_LEVEL_11_1: return TEXT("11_1");
case D3D_FEATURE_LEVEL_12_0: return TEXT("12_0");
case D3D_FEATURE_LEVEL_12_1: return TEXT("12_1");
case D3D_FEATURE_LEVEL_12_2: return TEXT("12_2");
}
return TEXT("X_X");
}
void FFindBestDevice::LoadSettings()
{
FParse::Value(FCommandLine::Get(), TEXT("-ExplicitAdapterValue="), ExplicitAdapterValue);
FParse::Value(FCommandLine::Get(), TEXT("-PreferedAdapterVendor="), PreferedAdapterVendor);
bAllowSoftwareRendering = FParse::Param(FCommandLine::Get(), TEXT("AllowSoftwareRendering"));
bPreferedMinimalPower = FParse::Param(FCommandLine::Get(), TEXT("PreferedMinimalPower"));
bPreferedHighPerformance = FParse::Param(FCommandLine::Get(), TEXT("PreferedHighPerformance"));
bDebug = FParse::Param(FCommandLine::Get(), TEXT("d3ddebug"));
CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)DXGIFactory1.GetInitReference());
if (DXGIFactory1)
{
DXGIFactory1->QueryInterface(__uuidof(IDXGIFactory6), (void**)DXGIFactory6.GetInitReference());
}
if (bPreferedMinimalPower)
{
GpuPreference = DXGI_GPU_PREFERENCE_MINIMUM_POWER;
}
else
{
GpuPreference = DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE;
}
UE_LOG(LogStandaloneRenderer, Log, TEXT("D3D11 adapters settings:"));
if (ExplicitAdapterValue >= 0)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("ExplicitAdapterValue=%d"), ExplicitAdapterValue);
}
if (PreferedAdapterVendor >= 0)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("PreferedAdapterVendor=%d"), PreferedAdapterVendor);
}
if (bAllowSoftwareRendering)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("bAllowSoftwareRendering=%s"), (bAllowSoftwareRendering ? TEXT("true") : TEXT("false")));
}
if (bPreferedMinimalPower)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("bPreferedMinimalPower=%s"), (bPreferedMinimalPower ? TEXT("true") : TEXT("false")));
}
else if (bPreferedHighPerformance)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("bPreferedHighPerformance=%s"), (bPreferedHighPerformance ? TEXT("true") : TEXT("false")));
}
if (bDebug)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("bPreferedHighPerformance=%s"), (bPreferedHighPerformance ? TEXT("true") : TEXT("false")));
}
}
HRESULT FFindBestDevice::EnumerateAdapters(UINT AdapterIndex, IDXGIAdapter** Adapter)
{
if (!DXGIFactory6 || GpuPreference == DXGI_GPU_PREFERENCE_UNSPECIFIED)
{
return DXGIFactory1->EnumAdapters(AdapterIndex, Adapter);
}
else
{
return DXGIFactory6->EnumAdapterByGpuPreference(AdapterIndex, (DXGI_GPU_PREFERENCE)GpuPreference, __uuidof(IDXGIAdapter), (void**)Adapter);
}
}
bool FFindBestDevice::CreateTesingDevice(IDXGIAdapter* Adapter, D3D_FEATURE_LEVEL& ResultFeatureLevel)
{
ID3D11Device* D3DDevice = nullptr;
ID3D11DeviceContext* D3DDeviceContext = nullptr;
uint32 DeviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED;
if (bDebug)
{
DeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
}
const D3D_FEATURE_LEVEL RequestedFeatureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 };
const int32 NumAllowedFeatureLevels = UE_ARRAY_COUNT(RequestedFeatureLevels);
ResultFeatureLevel = D3D_FEATURE_LEVEL_1_0_CORE;
__try
{
HRESULT CreateDeviceResult;
CreateDeviceResult = D3D11CreateDevice(Adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, DeviceFlags, RequestedFeatureLevels, NumAllowedFeatureLevels, D3D11_SDK_VERSION, &D3DDevice, &ResultFeatureLevel, &D3DDeviceContext);
if (SUCCEEDED(CreateDeviceResult))
{
D3DDevice->Release();
D3DDeviceContext->Release();
return true;
}
else
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("Failed to create device [0x%08X]"), CreateDeviceResult);
}
}
__except (Private::IsDelayLoadException(GetExceptionInformation()))
{
// We suppress warning C6322: Empty _except block. Appropriate checks are made upon returning.
CA_SUPPRESS(6322);
}
return false;
}
void FFindBestDevice::SelectAdapter()
{
TRefCountPtr<IDXGIAdapter> FirstDXGIAdapterWithoutIntegratedAdapter;
TRefCountPtr<IDXGIAdapter> FirstDXGIAdapterAdapter;
D3D_FEATURE_LEVEL FirstFeatureLevelWithoutIntegratedAdapter = D3D_FEATURE_LEVEL_1_0_CORE;
D3D_FEATURE_LEVEL FirstFeatureLeveAdapter = D3D_FEATURE_LEVEL_1_0_CORE;
UE_LOG(LogStandaloneRenderer, Log, TEXT("D3D11 adapters:"));
// Enumerate the DXGIFactory's adapters.
TRefCountPtr<IDXGIAdapter> TestingAdapter;
for (uint32 AdapterIndex = 0; EnumerateAdapters(AdapterIndex, TestingAdapter.GetInitReference()) != DXGI_ERROR_NOT_FOUND; ++AdapterIndex)
{
DXGI_ADAPTER_DESC AdapterDesc;
if (HRESULT DescResult = TestingAdapter->GetDesc(&AdapterDesc); FAILED(DescResult))
{
UE_LOG(LogStandaloneRenderer, Warning, TEXT("Failed to get description for adapter %u."), AdapterIndex);
}
else
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("Testing D3D11 adapter: %u. Description: '%s'. VendorId: %04x. DeviceId: %04x.")
, AdapterIndex, AdapterDesc.Description, AdapterDesc.VendorId, AdapterDesc.DeviceId);
}
D3D_FEATURE_LEVEL FeatureLevel;
if (!CreateTesingDevice(TestingAdapter, FeatureLevel))
{
UE_LOG(LogStandaloneRenderer, Log, TEXT(" Failed to create test device."), AdapterIndex);
continue;
}
UE_LOG(LogStandaloneRenderer, Log, TEXT(" %u. '%s'. Feature level: %s"), AdapterIndex, AdapterDesc.Description, GetFeatureLevelString(FeatureLevel));
const bool bIsMicrosoft = AdapterDesc.VendorId == 0x1414;
const bool bIsSoftware = bIsMicrosoft;
const bool bSkipSoftwareAdapter = bIsSoftware && !bAllowSoftwareRendering && ExplicitAdapterValue < 0;
const bool bSkipExplicitAdapter = ExplicitAdapterValue >= 0 && AdapterIndex != ExplicitAdapterValue;
const bool bSkipAdapter = bSkipSoftwareAdapter || bSkipExplicitAdapter;
if (bSkipAdapter)
{
UE_LOG(LogStandaloneRenderer, Log, TEXT(" Skip adapter."));
continue;
}
bool bIsNonLocalMemoryPresent = false;
{
TRefCountPtr<IDXGIAdapter3> TempDxgiAdapter3;
DXGI_QUERY_VIDEO_MEMORY_INFO NonLocalVideoMemoryInfo;
if (SUCCEEDED(TestingAdapter->QueryInterface(_uuidof(IDXGIAdapter3), (void**)TempDxgiAdapter3.GetInitReference())) &&
TempDxgiAdapter3.IsValid() && SUCCEEDED(TempDxgiAdapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &NonLocalVideoMemoryInfo)))
{
bIsNonLocalMemoryPresent = NonLocalVideoMemoryInfo.Budget != 0;
}
}
const bool bIsIntegrated = !bIsNonLocalMemoryPresent;
if (!bIsIntegrated && !FirstDXGIAdapterWithoutIntegratedAdapter.IsValid())
{
FirstDXGIAdapterWithoutIntegratedAdapter = TestingAdapter;
FirstFeatureLevelWithoutIntegratedAdapter = FeatureLevel;
}
else if (PreferedAdapterVendor == AdapterDesc.VendorId && FirstDXGIAdapterWithoutIntegratedAdapter.IsValid())
{
FirstDXGIAdapterWithoutIntegratedAdapter = TestingAdapter;
FirstFeatureLevelWithoutIntegratedAdapter = FeatureLevel;
}
if (!FirstDXGIAdapterAdapter.IsValid())
{
FirstDXGIAdapterAdapter = TestingAdapter;
FirstFeatureLeveAdapter = FeatureLevel;
}
else if (PreferedAdapterVendor == AdapterDesc.VendorId && FirstDXGIAdapterAdapter.IsValid())
{
FirstDXGIAdapterAdapter = TestingAdapter;
FirstFeatureLeveAdapter = FeatureLevel;
}
}
const bool bFavorNonIntegrated = ExplicitAdapterValue == -1;
if (bFavorNonIntegrated)
{
ChosenAdapter = FirstDXGIAdapterWithoutIntegratedAdapter;
ChosenFeatureLevel = FirstFeatureLevelWithoutIntegratedAdapter;
// We assume Intel is integrated graphics (slower than discrete) than NVIDIA or AMD cards and rather take a different one
if (!ChosenAdapter.IsValid())
{
ChosenAdapter = FirstDXGIAdapterAdapter;
ChosenFeatureLevel = FirstFeatureLeveAdapter;
}
}
else
{
ChosenAdapter = FirstDXGIAdapterAdapter;
ChosenFeatureLevel = FirstFeatureLeveAdapter;
}
if (ChosenAdapter.IsValid())
{
DXGI_ADAPTER_DESC AdapterDesc;
HRESULT DescResult = ChosenAdapter->GetDesc(&AdapterDesc);
if (FAILED(DescResult))
{
UE_LOG(LogStandaloneRenderer, Warning, TEXT("Failed to get description for selected adapter."));
}
else
{
UE_LOG(LogStandaloneRenderer, Log, TEXT("Selected D3D11 Description: '%s'. VendorId: %04x. DeviceId: %04x.")
, AdapterDesc.Description, AdapterDesc.VendorId, AdapterDesc.DeviceId);
}
}
}
} //namespace