You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb Luke.Thatcher #preflight 627cd9aece4192efbe2710ed [CL 20162706 by Andriy Tylychko in ue5-main branch]
711 lines
27 KiB
C++
711 lines
27 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
DynamicRHI.cpp: Dynamically bound Render Hardware Interface implementation.
|
|
=============================================================================*/
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Misc/OutputDeviceRedirector.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "Misc/App.h"
|
|
#include "RHI.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "GenericPlatform/GenericPlatformDriver.h"
|
|
#include "GenericPlatform/GenericPlatformCrashContext.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "TextureProfiler.h"
|
|
|
|
#if defined(NV_GEFORCENOW) && NV_GEFORCENOW
|
|
#include "GeForceNOWWrapper.h"
|
|
#endif
|
|
|
|
IMPLEMENT_TYPE_LAYOUT(FRayTracingGeometryInitializer);
|
|
IMPLEMENT_TYPE_LAYOUT(FRayTracingGeometrySegment);
|
|
|
|
static_assert(sizeof(FRayTracingGeometryInstance) <= 104,
|
|
"Ray tracing instance descriptor is expected to be no more than 104 bytes, as there may be a very large number of them.");
|
|
|
|
#ifndef PLATFORM_ALLOW_NULL_RHI
|
|
#define PLATFORM_ALLOW_NULL_RHI 0
|
|
#endif
|
|
|
|
// Globals.
|
|
FDynamicRHI* GDynamicRHI = NULL;
|
|
|
|
static TAutoConsoleVariable<int32> CVarWarnOfBadDrivers(
|
|
TEXT("r.WarnOfBadDrivers"),
|
|
1,
|
|
TEXT("On engine startup we can check the current GPU driver and warn the user about issues and suggest a specific version\n")
|
|
TEXT("The test is fast so this should not cost any performance.\n")
|
|
TEXT(" 0: off\n")
|
|
TEXT(" 1: a message on startup might appear (default)\n")
|
|
TEXT(" 2: Simulating the system has a NVIDIA driver on the deny list (UI should appear)\n")
|
|
TEXT(" 3: Simulating the system has a AMD driver on the deny list (UI should appear)\n")
|
|
TEXT(" 4: Simulating the system has an allowed AMD driver (no UI should appear)\n")
|
|
TEXT(" 5: Simulating the system has a Intel driver (no UI should appear)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarDisableDriverWarningPopupIfGFN(
|
|
TEXT("r.DisableDriverWarningPopupIfGFN"),
|
|
1,
|
|
TEXT("If non-zero, disable driver version warning popup if running on a GFN cloud machine."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
void InitNullRHI()
|
|
{
|
|
// Use the null RHI if it was specified on the command line, or if a commandlet is running.
|
|
IDynamicRHIModule* DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("NullDrv"));
|
|
// Create the dynamic RHI.
|
|
if ((DynamicRHIModule == 0) || !DynamicRHIModule->IsSupported())
|
|
{
|
|
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("DynamicRHI", "NullDrvFailure", "NullDrv failure?"));
|
|
FPlatformMisc::RequestExit(1);
|
|
}
|
|
|
|
GDynamicRHI = DynamicRHIModule->CreateRHI();
|
|
GDynamicRHI->Init();
|
|
|
|
// Command lists need the validation RHI context if enabled, so call the global scope version of RHIGetDefaultContext() and RHIGetDefaultAsyncComputeContext().
|
|
GRHICommandList.GetImmediateCommandList().SetContext(::RHIGetDefaultContext());
|
|
GRHICommandList.GetImmediateAsyncComputeCommandList().SetComputeContext(::RHIGetDefaultAsyncComputeContext());
|
|
|
|
GUsingNullRHI = true;
|
|
GRHISupportsTextureStreaming = false;
|
|
|
|
// Update the crash context analytics
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.RHIName"), TEXT("NullRHI"));
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS || PLATFORM_UNIX
|
|
static void RHIDetectAndWarnOfBadDrivers(bool bHasEditorToken)
|
|
{
|
|
int32 CVarValue = CVarWarnOfBadDrivers.GetValueOnGameThread();
|
|
|
|
if(!GIsRHIInitialized || !CVarValue || (GRHIVendorId == 0) || FApp::IsUnattended())
|
|
{
|
|
UE_LOG(LogRHI, Log, TEXT("Skipping Driver Check: RHI%s initialized, WarnOfBadDrivers=%d, VendorId=0x%x, is%s unattended"),
|
|
GIsRHIInitialized ? TEXT("") : TEXT(" NOT"), CVarValue, GRHIVendorId, FApp::IsUnattended() ? TEXT("") : TEXT(" NOT"));
|
|
return;
|
|
}
|
|
|
|
FGPUDriverInfo DriverInfo;
|
|
|
|
// later we should make the globals use the struct directly
|
|
DriverInfo.VendorId = GRHIVendorId;
|
|
DriverInfo.DeviceDescription = GRHIAdapterName;
|
|
DriverInfo.ProviderName = TEXT("Unknown");
|
|
DriverInfo.InternalDriverVersion = GRHIAdapterInternalDriverVersion;
|
|
DriverInfo.UserDriverVersion = GRHIAdapterUserDriverVersion;
|
|
DriverInfo.DriverDate = GRHIAdapterDriverDate;
|
|
DriverInfo.RHIName = GDynamicRHI ? GDynamicRHI->GetName() : FString();
|
|
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
// for testing
|
|
if(CVarValue == 2)
|
|
{
|
|
DriverInfo.SetNVIDIA();
|
|
DriverInfo.DeviceDescription = TEXT("Test NVIDIA (bad)");
|
|
DriverInfo.UserDriverVersion = TEXT("346.43");
|
|
DriverInfo.InternalDriverVersion = TEXT("9.18.134.643");
|
|
DriverInfo.DriverDate = TEXT("01-01-1900");
|
|
}
|
|
else if(CVarValue == 3)
|
|
{
|
|
DriverInfo.SetAMD();
|
|
DriverInfo.DeviceDescription = TEXT("Test AMD (bad)");
|
|
DriverInfo.UserDriverVersion = TEXT("Test Catalyst Version");
|
|
DriverInfo.InternalDriverVersion = TEXT("13.152.1.1000");
|
|
DriverInfo.DriverDate = TEXT("09-10-13");
|
|
}
|
|
else if(CVarValue == 4)
|
|
{
|
|
DriverInfo.SetAMD();
|
|
DriverInfo.DeviceDescription = TEXT("Test AMD (good)");
|
|
DriverInfo.UserDriverVersion = TEXT("Test Catalyst Version");
|
|
DriverInfo.InternalDriverVersion = TEXT("15.30.1025.1001");
|
|
DriverInfo.DriverDate = TEXT("01-01-16");
|
|
}
|
|
else if(CVarValue == 5)
|
|
{
|
|
DriverInfo.SetIntel();
|
|
DriverInfo.DeviceDescription = TEXT("Test Intel (good)");
|
|
DriverInfo.UserDriverVersion = TEXT("Test Intel Version");
|
|
DriverInfo.InternalDriverVersion = TEXT("8.15.10.2302");
|
|
DriverInfo.DriverDate = TEXT("01-01-15");
|
|
}
|
|
#endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
|
|
FGPUHardware DetectedGPUHardware(DriverInfo);
|
|
|
|
// Pre-GCN GPUs usually don't support updating to latest driver
|
|
// But it is unclear what is the latest version supported as it varies from card to card
|
|
// So just don't complain if pre-gcn
|
|
if (DriverInfo.IsValid() && !GRHIDeviceIsAMDPreGCNArchitecture)
|
|
{
|
|
FDriverDenyListEntry DenyListEntry = DetectedGPUHardware.FindDriverDenyListEntry();
|
|
|
|
TArray<FString> DeviceCanUpdateDriverList;
|
|
GConfig->GetArray(TEXT("Devices"), TEXT("DeviceCanUpdateDriverList"), DeviceCanUpdateDriverList, GHardwareIni);
|
|
|
|
bool bVendorHasEntries = false;
|
|
bool bDeviceCanUpdateDriver = false;
|
|
for (const FString& DeviceCanUpdateDriverString : DeviceCanUpdateDriverList)
|
|
{
|
|
const TCHAR* Line = *DeviceCanUpdateDriverString;
|
|
|
|
FString VendorId;
|
|
FParse::Value(Line + 1, TEXT("VendorId="), VendorId);
|
|
uint32 VendorIdInt = FParse::HexNumber(*VendorId);
|
|
|
|
FString DeviceId;
|
|
FParse::Value(Line + 1, TEXT("DeviceId="), DeviceId);
|
|
uint32 DeviceIdInt = FParse::HexNumber(*DeviceId);
|
|
|
|
bVendorHasEntries |= DriverInfo.VendorId && DriverInfo.VendorId == VendorIdInt;
|
|
|
|
if (DriverInfo.VendorId && GRHIDeviceId &&
|
|
DriverInfo.VendorId == VendorIdInt && GRHIDeviceId == DeviceIdInt)
|
|
{
|
|
bDeviceCanUpdateDriver = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GRHIAdapterDriverOnDenyList = DenyListEntry.IsValid();
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.DriverBlacklisted"), DenyListEntry.IsValid() ? TEXT("true") : TEXT("false"));
|
|
|
|
// Only alert users who are capable of updating their driver. Assume vendors with an empty list can always update.
|
|
bool bShowPrompt = bDeviceCanUpdateDriver || !bVendorHasEntries;
|
|
|
|
if (DenyListEntry.IsValid() && bShowPrompt)
|
|
{
|
|
bool bLatestDenied = DetectedGPUHardware.IsLatestDenied();
|
|
|
|
// Note: we don't localize the vendor's name.
|
|
FString VendorString = DriverInfo.ProviderName;
|
|
FText HyperlinkText;
|
|
if (DriverInfo.IsNVIDIA())
|
|
{
|
|
VendorString = TEXT("NVIDIA");
|
|
HyperlinkText = NSLOCTEXT("MessageDialog", "DriverDownloadLinkNVIDIA", "https://www.nvidia.com/en-us/geforce/drivers/");
|
|
}
|
|
else if (DriverInfo.IsAMD())
|
|
{
|
|
VendorString = TEXT("AMD");
|
|
HyperlinkText = NSLOCTEXT("MessageDialog", "DriverDownloadLinkAMD", "https://www.amd.com/en/support");
|
|
}
|
|
else if (DriverInfo.IsIntel())
|
|
{
|
|
VendorString = TEXT("Intel");
|
|
HyperlinkText = NSLOCTEXT("MessageDialog", "DriverDownloadLinkIntel", "https://downloadcenter.intel.com/product/80939/Graphics");
|
|
}
|
|
|
|
// format message box UI
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("AdapterName"), FText::FromString(DriverInfo.DeviceDescription));
|
|
Args.Add(TEXT("Vendor"), FText::FromString(VendorString));
|
|
Args.Add(TEXT("RHI"), FText::FromString(DenyListEntry.RHIName));
|
|
Args.Add(TEXT("Hyperlink"), HyperlinkText);
|
|
Args.Add(TEXT("RecommendedVer"), FText::FromString(DetectedGPUHardware.GetSuggestedDriverVersion(DriverInfo.RHIName)));
|
|
Args.Add(TEXT("InstalledVer"), FText::FromString(DriverInfo.UserDriverVersion));
|
|
|
|
// this message can be suppressed with r.WarnOfBadDrivers=0
|
|
FText LocalizedMsg;
|
|
if (bLatestDenied)
|
|
{
|
|
if (!DenyListEntry.RHIName.IsEmpty())
|
|
{
|
|
LocalizedMsg = FText::Format(NSLOCTEXT("MessageDialog", "LatestVideoCardDriverRHIIssueReport", "The latest version of the {Vendor} graphics driver has known issues in {RHI}.\nPlease install the recommended driver version or switch to a different rendering API.\n\nWould you like to visit the following URL to download the driver?\n\n{Hyperlink}\n\n{AdapterName}\nInstalled: {InstalledVer}\nRecommended: {RecommendedVer}"), Args);
|
|
}
|
|
else
|
|
{
|
|
LocalizedMsg = FText::Format(NSLOCTEXT("MessageDialog", "LatestVideoCardDriverIssueReport", "The latest version of the {Vendor} graphics driver has known issues.\nPlease install the recommended driver version.\n\nWould you like to visit the following URL to download the driver?\n\n{Hyperlink}\n\n{AdapterName}\nInstalled: {InstalledVer}\nRecommended: {RecommendedVer}"), Args);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!DenyListEntry.RHIName.IsEmpty())
|
|
{
|
|
LocalizedMsg = FText::Format(NSLOCTEXT("MessageDialog", "VideoCardDriverRHIIssueReport", "The installed version of the {Vendor} graphics driver has known issues in {RHI}.\nPlease install either the latest or the recommended driver version or switch to a different rendering API.\n\nWould you like to visit the following URL to download the driver?\n\n{Hyperlink}\n\n{AdapterName}\nInstalled: {InstalledVer}\nRecommended: {RecommendedVer}"), Args);
|
|
}
|
|
else
|
|
{
|
|
LocalizedMsg = FText::Format(NSLOCTEXT("MessageDialog", "VideoCardDriverIssueReport", "The installed version of the {Vendor} graphics driver has known issues.\nPlease install either the latest or the recommended driver version.\n\nWould you like to visit the following URL to download the driver?\n\n{Hyperlink}\n\n{AdapterName}\nInstalled: {InstalledVer}\nRecommended: {RecommendedVer}"), Args);
|
|
}
|
|
}
|
|
|
|
FText Title = NSLOCTEXT("MessageDialog", "TitleVideoCardDriverIssue", "WARNING: Known issues with graphics driver");
|
|
EAppReturnType::Type Response = FMessageDialog::Open(EAppMsgType::YesNo, LocalizedMsg, &Title);
|
|
if (Response == EAppReturnType::Yes)
|
|
{
|
|
FPlatformProcess::LaunchURL(*HyperlinkText.ToString(), nullptr, nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#elif PLATFORM_MAC
|
|
static void RHIDetectAndWarnOfBadDrivers(bool bHasEditorToken)
|
|
{
|
|
int32 CVarValue = CVarWarnOfBadDrivers.GetValueOnGameThread();
|
|
|
|
if (!GIsRHIInitialized || !CVarValue || GRHIVendorId == 0 || bHasEditorToken || FApp::IsUnattended())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (FPlatformMisc::MacOSXVersionCompare(10,15,5) < 0)
|
|
{
|
|
// this message can be suppressed with r.WarnOfBadDrivers=0
|
|
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok,
|
|
*NSLOCTEXT("MessageDialog", "UpdateMacOSX_Body", "Please update to the latest version of macOS for best performance and stability.").ToString(),
|
|
*NSLOCTEXT("MessageDialog", "UpdateMacOSX_Title", "Update macOS").ToString());
|
|
}
|
|
}
|
|
#endif // PLATFORM_WINDOWS
|
|
|
|
void RHIInit(bool bHasEditorToken)
|
|
{
|
|
if (!GDynamicRHI)
|
|
{
|
|
#if RHI_ENABLE_RESOURCE_INFO
|
|
FRHIResource::StartTrackingAllResources();
|
|
#endif
|
|
|
|
// read in any data driven shader platform info structures we can find
|
|
FGenericDataDrivenShaderPlatformInfo::Initialize();
|
|
|
|
GRHICommandList.LatchBypass(); // read commandline for bypass flag
|
|
|
|
if (!FApp::CanEverRender())
|
|
{
|
|
InitNullRHI();
|
|
}
|
|
else
|
|
{
|
|
LLM_SCOPE(ELLMTag::RHIMisc);
|
|
|
|
GDynamicRHI = PlatformCreateDynamicRHI();
|
|
if (GDynamicRHI)
|
|
{
|
|
GDynamicRHI->Init();
|
|
|
|
#if WITH_MGPU
|
|
AFRUtils::StaticInitialize();
|
|
#endif
|
|
|
|
// Validation of contexts.
|
|
GRHICommandList.GetImmediateCommandList().GetContext();
|
|
GRHICommandList.GetImmediateAsyncComputeCommandList().GetComputeContext();
|
|
check(GIsRHIInitialized);
|
|
|
|
// Set default GPU mask to all GPUs. This is necessary to ensure that any commands
|
|
// that create and initialize resources are executed on all GPUs. Scene rendering
|
|
// will restrict itself to a subset of GPUs as needed.
|
|
GRHICommandList.GetImmediateCommandList().SetGPUMask(FRHIGPUMask::All());
|
|
GRHICommandList.GetImmediateAsyncComputeCommandList().SetGPUMask(FRHIGPUMask::All());
|
|
|
|
FString FeatureLevelString;
|
|
GetFeatureLevelName(GMaxRHIFeatureLevel, FeatureLevelString);
|
|
|
|
if (bHasEditorToken && GMaxRHIFeatureLevel < ERHIFeatureLevel::SM5)
|
|
{
|
|
FString ShaderPlatformString = LegacyShaderPlatformToShaderFormat(GetFeatureLevelShaderPlatform(GMaxRHIFeatureLevel)).ToString();
|
|
FString Error = FString::Printf(TEXT("A Feature Level 5 video card is required to run the editor.\nAvailableFeatureLevel = %s, ShaderPlatform = %s"), *FeatureLevelString, *ShaderPlatformString);
|
|
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Error));
|
|
FPlatformMisc::RequestExit(1);
|
|
}
|
|
|
|
// Update the crash context analytics
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.RHIName"), GDynamicRHI ? (GMaxRHIFeatureLevel == ERHIFeatureLevel::ES3_1 ? FString(GDynamicRHI->GetName()) + TEXT("_ES31") : GDynamicRHI->GetName()) : TEXT("Unknown"));
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.AdapterName"), GRHIAdapterName);
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.UserDriverVersion"), GRHIAdapterUserDriverVersion);
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.InternalDriverVersion"), GRHIAdapterInternalDriverVersion);
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.DriverDate"), GRHIAdapterDriverDate);
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.FeatureLevel"), FeatureLevelString);
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.GPUVendor"), RHIVendorIdToString());
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.DeviceId"), FString::Printf(TEXT("%04X"), GRHIDeviceId));
|
|
|
|
#if TEXTURE_PROFILER_ENABLED
|
|
FTextureProfiler::Get()->Init();
|
|
#endif
|
|
}
|
|
#if PLATFORM_ALLOW_NULL_RHI
|
|
else
|
|
{
|
|
// If the platform supports doing so, fall back to the NULL RHI on failure
|
|
InitNullRHI();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
check(GDynamicRHI);
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_UNIX
|
|
#if defined(NV_GEFORCENOW) && NV_GEFORCENOW
|
|
bool bDetectAndWarnBadDrivers = true;
|
|
if (IsRHIDeviceNVIDIA() && !!CVarDisableDriverWarningPopupIfGFN.GetValueOnAnyThread())
|
|
{
|
|
const GfnRuntimeError GfnResult = GeForceNOWWrapper::Get().Initialize();
|
|
const bool bGfnRuntimeSDKInitialized = GfnResult == gfnSuccess || GfnResult == gfnInitSuccessClientOnly;
|
|
if (bGfnRuntimeSDKInitialized)
|
|
{
|
|
UE_LOG(LogRHI, Log, TEXT("GeForceNow SDK initialized: %d"), (int32)GfnResult);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogRHI, Log, TEXT("GeForceNow SDK initialization failed: %d"), (int32)GfnResult);
|
|
}
|
|
|
|
// Don't pop up a driver version warning window when running on a cloud machine
|
|
bDetectAndWarnBadDrivers = !bGfnRuntimeSDKInitialized || !GeForceNOWWrapper::Get().IsRunningInCloud();
|
|
|
|
if (GeForceNOWWrapper::Get().IsRunningInGFN())
|
|
{
|
|
FGenericCrashContext::SetEngineData(TEXT("RHI.CloudInstance"), TEXT("GeForceNow"));
|
|
}
|
|
}
|
|
|
|
if (bDetectAndWarnBadDrivers)
|
|
{
|
|
RHIDetectAndWarnOfBadDrivers(bHasEditorToken);
|
|
}
|
|
#else
|
|
RHIDetectAndWarnOfBadDrivers(bHasEditorToken);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void RHIPostInit(const TArray<uint32>& InPixelFormatByteWidth)
|
|
{
|
|
check(GDynamicRHI);
|
|
GDynamicRHI->InitPixelFormatInfo(InPixelFormatByteWidth);
|
|
GDynamicRHI->PostInit();
|
|
}
|
|
|
|
void RHIExit()
|
|
{
|
|
if (!GUsingNullRHI && GDynamicRHI != NULL)
|
|
{
|
|
// Clean up all cached pipelines
|
|
PipelineStateCache::Shutdown();
|
|
|
|
// Flush any potential commands queued before we shut things down.
|
|
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
|
|
|
// Destruct the dynamic RHI.
|
|
GDynamicRHI->Shutdown();
|
|
delete GDynamicRHI;
|
|
GDynamicRHI = NULL;
|
|
|
|
#if RHI_ENABLE_RESOURCE_INFO
|
|
FRHIResource::StopTrackingAllResources();
|
|
#endif
|
|
}
|
|
else if (GUsingNullRHI)
|
|
{
|
|
// If we are using NullRHI flush the command list here in case somethings has been added to the command list during exit calls
|
|
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
|
|
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
|
}
|
|
|
|
void CleanupRHICommandListGraphEvents();
|
|
CleanupRHICommandListGraphEvents();
|
|
}
|
|
|
|
|
|
static void BaseRHISetGPUCaptureOptions(const TArray<FString>& Args, UWorld* World)
|
|
{
|
|
if (Args.Num() > 0)
|
|
{
|
|
const bool bEnabled = Args[0].ToBool();
|
|
GDynamicRHI->EnableIdealGPUCaptureOptions(bEnabled);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogRHI, Display, TEXT("Usage: r.RHISetGPUCaptureOptions 0 or r.RHISetGPUCaptureOptions 1"));
|
|
}
|
|
}
|
|
|
|
static FAutoConsoleCommandWithWorldAndArgs GBaseRHISetGPUCaptureOptions(
|
|
TEXT("r.RHISetGPUCaptureOptions"),
|
|
TEXT("Utility function to change multiple CVARs useful when profiling or debugging GPU rendering. Setting to 1 or 0 will guarantee all options are in the appropriate state.\n")
|
|
TEXT("r.rhithread.enable, r.rhicmdbypass, r.showmaterialdrawevents, toggledrawevents\n")
|
|
TEXT("Platform RHI's may implement more feature toggles."),
|
|
FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&BaseRHISetGPUCaptureOptions)
|
|
);
|
|
|
|
void FDynamicRHI::RHIReadSurfaceFloatData(FRHITexture* Texture, FIntRect Rect, TArray<FFloat16Color>& OutData, FReadSurfaceDataFlags InFlags)
|
|
{
|
|
#if WITH_MGPU
|
|
if (InFlags.GetGPUIndex() != 0)
|
|
{
|
|
unimplemented();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
RHIReadSurfaceFloatData(Texture, Rect, OutData, InFlags.GetCubeFace(), InFlags.GetArrayIndex(), InFlags.GetMip());
|
|
}
|
|
}
|
|
|
|
void FDynamicRHI::RHIRead3DSurfaceFloatData(FRHITexture* Texture, FIntRect Rect, FIntPoint ZMinMax, TArray<FFloat16Color>& OutData, FReadSurfaceDataFlags InFlags)
|
|
{
|
|
#if WITH_MGPU
|
|
if (InFlags.GetGPUIndex() != 0)
|
|
{
|
|
unimplemented();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
RHIRead3DSurfaceFloatData(Texture, Rect, ZMinMax, OutData);
|
|
}
|
|
}
|
|
|
|
void FDynamicRHI::EnableIdealGPUCaptureOptions(bool bEnabled)
|
|
{
|
|
static IConsoleVariable* RHICmdBypassVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.rhicmdbypass"));
|
|
static IConsoleVariable* ShowMaterialDrawEventVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ShowMaterialDrawEvents"));
|
|
static IConsoleObject* RHIThreadEnableObj = IConsoleManager::Get().FindConsoleObject(TEXT("r.RHIThread.Enable"));
|
|
static IConsoleCommand* RHIThreadEnableCommand = RHIThreadEnableObj ? RHIThreadEnableObj->AsCommand() : nullptr;
|
|
|
|
const bool bShouldEnableDrawEvents = bEnabled;
|
|
const bool bShouldEnableMaterialDrawEvents = bEnabled;
|
|
const bool bShouldEnableRHIThread = !bEnabled;
|
|
const bool bShouldRHICmdBypass = bEnabled;
|
|
|
|
const bool bDrawEvents = GetEmitDrawEvents() != 0;
|
|
const bool bMaterialDrawEvents = ShowMaterialDrawEventVar ? ShowMaterialDrawEventVar->GetInt() != 0 : false;
|
|
const bool bRHIThread = IsRunningRHIInSeparateThread();
|
|
const bool bRHIBypass = RHICmdBypassVar ? RHICmdBypassVar->GetInt() != 0 : false;
|
|
|
|
UE_LOG(LogRHI, Display, TEXT("Setting GPU Capture Options: %i"), bEnabled ? 1 : 0);
|
|
if (bShouldEnableDrawEvents != bDrawEvents)
|
|
{
|
|
UE_LOG(LogRHI, Display, TEXT("Toggling draw events: %i"), bShouldEnableDrawEvents ? 1 : 0);
|
|
SetEmitDrawEvents(bShouldEnableDrawEvents);
|
|
}
|
|
if (bShouldEnableMaterialDrawEvents != bMaterialDrawEvents && ShowMaterialDrawEventVar)
|
|
{
|
|
UE_LOG(LogRHI, Display, TEXT("Toggling showmaterialdrawevents: %i"), bShouldEnableDrawEvents ? 1 : 0);
|
|
ShowMaterialDrawEventVar->Set(bShouldEnableDrawEvents ? -1 : 0);
|
|
}
|
|
if (bRHIThread != bShouldEnableRHIThread && RHIThreadEnableCommand)
|
|
{
|
|
UE_LOG(LogRHI, Display, TEXT("Toggling rhi thread: %i"), bShouldEnableRHIThread ? 1 : 0);
|
|
TArray<FString> Args;
|
|
Args.Add(FString::Printf(TEXT("%i"), bShouldEnableRHIThread ? 1 : 0));
|
|
RHIThreadEnableCommand->Execute(Args, nullptr, *GLog);
|
|
}
|
|
if (bRHIBypass != bShouldRHICmdBypass && RHICmdBypassVar)
|
|
{
|
|
UE_LOG(LogRHI, Display, TEXT("Toggling rhi bypass: %i"), bEnabled ? 1 : 0);
|
|
RHICmdBypassVar->Set(bShouldRHICmdBypass ? 1 : 0, ECVF_SetByConsole);
|
|
}
|
|
}
|
|
|
|
void FDynamicRHI::RHITransferBufferUnderlyingResource(FRHIBuffer* DestBuffer, FRHIBuffer* SrcBuffer)
|
|
{
|
|
UE_LOG(LogRHI, Fatal, TEXT("RHITransferBufferUnderlyingResource isn't implemented for the current RHI"));
|
|
}
|
|
|
|
FUnorderedAccessViewRHIRef FDynamicRHI::RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel)
|
|
{
|
|
return RHICreateUnorderedAccessView(Texture, MipLevel, 0, 0);
|
|
}
|
|
|
|
FUnorderedAccessViewRHIRef FDynamicRHI::RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel, uint8 Format)
|
|
{
|
|
return RHICreateUnorderedAccessView(Texture, MipLevel, Format, 0, 0);
|
|
}
|
|
|
|
FUnorderedAccessViewRHIRef FDynamicRHI::RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel, uint8 Format, uint16 FirstArraySlice, uint16 NumArraySlices)
|
|
{
|
|
UE_LOG(LogRHI, Fatal, TEXT("RHICreateUnorderedAccessView with Format parameter isn't implemented for the current RHI"));
|
|
return RHICreateUnorderedAccessView(Texture, MipLevel, FirstArraySlice, NumArraySlices);
|
|
}
|
|
|
|
void FDynamicRHI::RHIUpdateShaderResourceView(FRHIShaderResourceView* SRV, FRHIBuffer* Buffer, uint32 Stride, uint8 Format)
|
|
{
|
|
UE_LOG(LogRHI, Fatal, TEXT("RHIUpdateShaderResourceView isn't implemented for the current RHI"));
|
|
}
|
|
|
|
void FDynamicRHI::RHIUpdateShaderResourceView(FRHIShaderResourceView* SRV, FRHIBuffer* Buffer)
|
|
{
|
|
UE_LOG(LogRHI, Fatal, TEXT("RHIUpdateShaderResourceView isn't implemented for the current RHI"));
|
|
}
|
|
|
|
uint64 FDynamicRHI::RHIGetMinimumAlignmentForBufferBackedSRV(EPixelFormat Format)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
FDefaultRHIRenderQueryPool::FDefaultRHIRenderQueryPool(ERenderQueryType InQueryType, FDynamicRHI* InDynamicRHI, uint32 InNumQueries)
|
|
: DynamicRHI(InDynamicRHI)
|
|
, QueryType(InQueryType)
|
|
, NumQueries(InNumQueries)
|
|
{
|
|
if (NumQueries != UINT32_MAX && (GSupportsTimestampRenderQueries || InQueryType != RQT_AbsoluteTime))
|
|
{
|
|
Queries.Reserve(NumQueries);
|
|
for (uint32 i = 0; i < NumQueries; i++)
|
|
{
|
|
Queries.Push(DynamicRHI->RHICreateRenderQuery(QueryType));
|
|
check(Queries.Last().IsValid());
|
|
++AllocatedQueries;
|
|
}
|
|
}
|
|
}
|
|
|
|
FDefaultRHIRenderQueryPool::~FDefaultRHIRenderQueryPool()
|
|
{
|
|
check(IsInRHIThread() || IsInRenderingThread());
|
|
checkf(AllocatedQueries == Queries.Num(), TEXT("Querypool deleted before all Queries have been released"));
|
|
}
|
|
|
|
FRHIPooledRenderQuery FDefaultRHIRenderQueryPool::AllocateQuery()
|
|
{
|
|
check(IsInParallelRenderingThread());
|
|
if (Queries.Num() > 0)
|
|
{
|
|
return FRHIPooledRenderQuery(this, Queries.Pop());
|
|
}
|
|
else
|
|
{
|
|
FRHIPooledRenderQuery Query = FRHIPooledRenderQuery(this, DynamicRHI->RHICreateRenderQuery(QueryType));
|
|
if (Query.IsValid())
|
|
{
|
|
++AllocatedQueries;
|
|
}
|
|
ensure(AllocatedQueries <= NumQueries);
|
|
return Query;
|
|
}
|
|
}
|
|
|
|
void FDefaultRHIRenderQueryPool::ReleaseQuery(TRefCountPtr<FRHIRenderQuery>&& Query)
|
|
{
|
|
if (QueryType == ERenderQueryType::RQT_Occlusion)
|
|
{
|
|
static int dbg = 0;
|
|
dbg++;
|
|
}
|
|
check(IsInParallelRenderingThread());
|
|
//Hard to validate because of Resource resurrection, better to remove GetQueryRef entirely
|
|
//checkf(Query.IsValid() && Query.GetRefCount() <= 2, TEXT("Query has been released but reference still held: use FRHIPooledRenderQuery::GetQueryRef() with extreme caution"));
|
|
|
|
checkf(Query.IsValid(), TEXT("Only release valid queries"));
|
|
checkf((uint32)Queries.Num() < NumQueries, TEXT("Pool contains more queries than it started with, double release somewhere?"));
|
|
|
|
Queries.Push(MoveTemp(Query));
|
|
check(!Query.IsValid());
|
|
}
|
|
|
|
FRenderQueryPoolRHIRef RHICreateRenderQueryPool(ERenderQueryType QueryType, uint32 NumQueries)
|
|
{
|
|
return GDynamicRHI->RHICreateRenderQueryPool(QueryType, NumQueries);
|
|
}
|
|
|
|
EColorSpaceAndEOTF FDynamicRHI::RHIGetColorSpace(FRHIViewport* Viewport)
|
|
{
|
|
return EColorSpaceAndEOTF::ERec709_sRGB;
|
|
}
|
|
|
|
void FDynamicRHI::RHICheckViewportHDRStatus(FRHIViewport* Viewport)
|
|
{
|
|
}
|
|
|
|
void* FDynamicRHI::RHILockBufferMGPU(FRHICommandListImmediate& RHICmdList, FRHIBuffer* Buffer, uint32 GPUIndex, uint32 Offset, uint32 Size, EResourceLockMode LockMode)
|
|
{
|
|
// Fall through to single GPU case
|
|
check(GPUIndex == 0);
|
|
return RHILockBuffer(RHICmdList, Buffer, Offset, Size, LockMode);
|
|
}
|
|
|
|
void FDynamicRHI::RHIUnlockBufferMGPU(FRHICommandListImmediate& RHICmdList, FRHIBuffer* Buffer, uint32 GPUIndex)
|
|
{
|
|
// Fall through to single GPU case
|
|
check(GPUIndex == 0);
|
|
RHIUnlockBuffer(RHICmdList, Buffer);
|
|
}
|
|
|
|
FShaderResourceViewInitializer::FShaderResourceViewInitializer(FRHIBuffer* InBuffer, EPixelFormat InFormat, uint32 InStartOffsetBytes, uint32 InNumElements)
|
|
: BufferInitializer({ InBuffer, InStartOffsetBytes, InNumElements, InFormat }), Type(EType::VertexBufferSRV)
|
|
{
|
|
check(InStartOffsetBytes % RHIGetMinimumAlignmentForBufferBackedSRV(InFormat) == 0);
|
|
/*if (!BufferInitializer.IsWholeResource())
|
|
{
|
|
const uint32 Stride = GPixelFormats[InFormat].BlockBytes;
|
|
check((BufferInitializer.NumElements * Stride + BufferInitializer.StartOffsetBytes) <= BufferInitializer.Buffer->GetSize());
|
|
}*/
|
|
|
|
InitType();
|
|
}
|
|
|
|
FShaderResourceViewInitializer::FShaderResourceViewInitializer(FRHIBuffer* InBuffer, EPixelFormat InFormat)
|
|
: BufferInitializer({ InBuffer, 0, UINT32_MAX, InFormat }), Type(EType::VertexBufferSRV)
|
|
{
|
|
InitType();
|
|
}
|
|
|
|
FShaderResourceViewInitializer::FShaderResourceViewInitializer(FRHIBuffer* InBuffer, uint32 InStartOffsetBytes, uint32 InNumElements)
|
|
: BufferInitializer({ InBuffer, InStartOffsetBytes, InNumElements, PF_Unknown }), Type(EType::StructuredBufferSRV)
|
|
{
|
|
const uint32 Stride = EnumHasAnyFlags(InBuffer->GetUsage(), BUF_AccelerationStructure)
|
|
? 1 // Acceleration structure buffers don't have a stride as they are opaque and not indexable
|
|
: InBuffer->GetStride();
|
|
|
|
check(InStartOffsetBytes % Stride == 0);
|
|
if (!BufferInitializer.IsWholeResource())
|
|
{
|
|
check((BufferInitializer.NumElements * Stride + BufferInitializer.StartOffsetBytes) <= BufferInitializer.Buffer->GetSize());
|
|
}
|
|
|
|
InitType();
|
|
}
|
|
|
|
FShaderResourceViewInitializer::FShaderResourceViewInitializer(FRHIBuffer* InBuffer)
|
|
: BufferInitializer({ InBuffer, 0, UINT32_MAX }), Type(EType::StructuredBufferSRV)
|
|
{
|
|
InitType();
|
|
}
|
|
|
|
FRawBufferShaderResourceViewInitializer::FRawBufferShaderResourceViewInitializer(FRHIBuffer* InBuffer)
|
|
: FShaderResourceViewInitializer(nullptr)
|
|
{
|
|
check(GRHISupportsRawViewsForAnyBuffer);
|
|
|
|
Type = EType::RawBufferSRV;
|
|
|
|
BufferInitializer.Buffer = InBuffer;
|
|
BufferInitializer.Format = PF_Unknown;
|
|
BufferInitializer.StartOffsetBytes = 0;
|
|
BufferInitializer.NumElements = UINT32_MAX; // Whole resource
|
|
}
|
|
|
|
void FShaderResourceViewInitializer::InitType()
|
|
{
|
|
if (BufferInitializer.Buffer)
|
|
{
|
|
EBufferUsageFlags Usage = BufferInitializer.Buffer->GetUsage();
|
|
if (EnumHasAnyFlags(Usage, BUF_VertexBuffer))
|
|
{
|
|
Type = EType::VertexBufferSRV;
|
|
}
|
|
else if (EnumHasAnyFlags(Usage, BUF_IndexBuffer))
|
|
{
|
|
Type = EType::IndexBufferSRV;
|
|
}
|
|
else if (EnumHasAnyFlags(Usage, BUF_AccelerationStructure))
|
|
{
|
|
Type = EType::AccelerationStructureSRV;
|
|
}
|
|
else
|
|
{
|
|
Type = EType::StructuredBufferSRV;
|
|
}
|
|
}
|
|
}
|