/* * Copyright (c) 2022 - 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual * property and proprietary rights in and to this material, related * documentation and any modifications thereto. Any use, reproduction, * disclosure or distribution of this material and related documentation * without an express license agreement from NVIDIA CORPORATION or * its affiliates is strictly prohibited. */ #include "StreamlineLibraryDLSSG.h" #include "StreamlineLibraryPrivate.h" #if WITH_STREAMLINE #include "StreamlineCore.h" #include "StreamlineRHI.h" #include "StreamlineReflex.h" #include "StreamlineDLSSG.h" #include "StreamlineAPI.h" #include "sl.h" #include "sl_dlss_g.h" #endif #include "Modules/ModuleManager.h" #include "Interfaces/IPluginManager.h" #define LOCTEXT_NAMESPACE "FStreammlineBlueprintModule" static const FName SetDLSSGModeInvalidEnumValueError= FName("SetDLSSGModeInvalidEnumValueError"); static const FName IsDLSSGModeSupportedInvalidEnumValueError = FName("IsDLSSGModeSupportedInvalidEnumValueError"); UStreamlineFeatureSupport UStreamlineLibraryDLSSG::DLSSGSupport = UStreamlineFeatureSupport::NotSupportedByPlatformAtBuildTime; #if WITH_STREAMLINE bool UStreamlineLibraryDLSSG::bDLSSGLibraryInitialized = false; static bool ShowDLSSSDebugOnScreenMessages() { return true; //if (GetDefault()->ShowDLSSSDebugOnScreenMessages == EDLSSSettingOverride::UseProjectSettings) //{ // return GetDefault()->bLogStreamlineBlueprint; //} //else //{ // return GetDefault()->ShowDLSSSDebugOnScreenMessages == EDLSSSettingOverride::Enabled; //} } #if !UE_BUILD_SHIPPING UStreamlineLibraryDLSSG::FDLSSErrorState UStreamlineLibraryDLSSG::DLSSErrorState; FDelegateHandle UStreamlineLibraryDLSSG::DLSSOnScreenMessagesDelegateHandle; void UStreamlineLibraryDLSSG::GetDLSSOnScreenMessages(TMultiMap& OutMessages) { check(IsInGameThread()); // We need a valid DLSSSupport, so calling this here in case other UStreamlineLibraryDLSSG functions which call TryInitStreamlineLibrary() haven't been called if (!TryInitDLSSGLibrary()) { return; } // TODO //if(ShowDLSSSDebugOnScreenMessages()) //{ // //} } #endif #endif bool UStreamlineLibraryDLSSG::IsDLSSGModeSupported(UStreamlineDLSSGMode DLSSGMode) { const UEnum* Enum = StaticEnum(); // UEnums are strongly typed, but then one can also cast a byte to an UEnum ... if (Enum->IsValidEnumValue(int64(DLSSGMode)) && (Enum->GetMaxEnumValue() != int64(DLSSGMode))) { if (DLSSGMode == UStreamlineDLSSGMode::Off) { return true; } #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("IsDLSSGModeSupported should not be called before PostEngineInit")); return false; } if (!IsDLSSGSupported()) { return false; } else { return true; // TODO } #else return false; #endif } else { #if !UE_BUILD_SHIPPING FFrame::KismetExecutionMessage(*FString::Printf( TEXT("IsDLSSGModeSupported should not be called with an invalid DLSSGMode enum value (%d) \"%s\""), int64(DLSSGMode), *StaticEnum()->GetDisplayNameTextByValue(int64(DLSSGMode)).ToString()), ELogVerbosity::Error, IsDLSSGModeSupportedInvalidEnumValueError); #endif return false; } } TArray UStreamlineLibraryDLSSG::GetSupportedDLSSGModes() { TArray SupportedQualityModes; #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("GetSupportedDLSSGModes should not be called before PostEngineInit")); return SupportedQualityModes; } #endif { const UEnum* Enum = StaticEnum(); for (int32 EnumIndex = 0; EnumIndex < Enum->NumEnums(); ++EnumIndex) { const int64 EnumValue = Enum->GetValueByIndex(EnumIndex); if (EnumValue != Enum->GetMaxEnumValue()) { const UStreamlineDLSSGMode QualityMode = UStreamlineDLSSGMode(EnumValue); if (IsDLSSGModeSupported(QualityMode)) { SupportedQualityModes.Add(QualityMode); } } } } return SupportedQualityModes; } bool UStreamlineLibraryDLSSG::IsDLSSGSupported() { #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("IsDLSSGSupported should not be called before PostEngineInit")); return false; } return QueryDLSSGSupport() == UStreamlineFeatureSupport::Supported; #else return false; #endif } UStreamlineFeatureSupport UStreamlineLibraryDLSSG::QueryDLSSGSupport() { #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("QueryDLSSGSupport should not be called before PostEngineInit")); return UStreamlineFeatureSupport::NotSupported; } #endif return DLSSGSupport; } #if WITH_STREAMLINE static int32 DLSSGModeIntCvarFromEnum(UStreamlineDLSSGMode DLSSGMode) { switch (DLSSGMode) { case UStreamlineDLSSGMode::Off: return 0; case UStreamlineDLSSGMode::On: return 1; case UStreamlineDLSSGMode::Auto: return 2; default: checkf(false, TEXT("dear DLSS-FG plugin developer, please support new enum type!")); return 0; } } static UStreamlineDLSSGMode DLSSGModeEnumFromIntCvar(int32 DLSSGMode) { switch (DLSSGMode) { case 0: return UStreamlineDLSSGMode::Off; case 1: return UStreamlineDLSSGMode::On; case 2: return UStreamlineDLSSGMode::Auto; default: UE_LOG(LogStreamlineBlueprint, Error, TEXT("Invalid r.Streamline.DLSSG.Enable value %d"), DLSSGMode); return UStreamlineDLSSGMode::Off; } } #endif // WITH_STREAMLINE void UStreamlineLibraryDLSSG::SetDLSSGMode(UStreamlineDLSSGMode DLSSGMode) { #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("SetDLSSGMode should not be called before PostEngineInit")); return; } const UEnum* Enum = StaticEnum(); // UEnums are strongly typed, but then one can also cast a byte to an UEnum ... if(Enum->IsValidEnumValue(int64(DLSSGMode)) && (Enum->GetMaxEnumValue() != int64(DLSSGMode))) { static auto CVarDLSSGEnable = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streamline.DLSSG.Enable")); if (CVarDLSSGEnable) { CVarDLSSGEnable->SetWithCurrentPriority(DLSSGModeIntCvarFromEnum(DLSSGMode)); } if (DLSSGMode != UStreamlineDLSSGMode::Off) { #if !UE_BUILD_SHIPPING check(IsInGameThread()); DLSSErrorState.bIsDLSSGModeUnsupported = !IsDLSSGModeSupported(DLSSGMode); DLSSErrorState.InvalidDLSSGMode = DLSSGMode; #endif } } else { #if !UE_BUILD_SHIPPING FFrame::KismetExecutionMessage(*FString::Printf( TEXT("SetDLSSGMode should not be called with an invalid DLSSGMode enum value (%d) \"%s\""), int64(DLSSGMode), *StaticEnum()->GetDisplayNameTextByValue(int64(DLSSGMode)).ToString()), ELogVerbosity::Error, SetDLSSGModeInvalidEnumValueError); #endif } #endif // WITH_STREAMLINE } STREAMLINEBLUEPRINT_API void UStreamlineLibraryDLSSG::GetDLSSGFrameTiming(float& FrameRateInHertz, int32& FramesPresented) { #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("GetDLSSGFrameTiming should not be called before PostEngineInit")); return; } GetStreamlineDLSSGFrameTiming(FrameRateInHertz, FramesPresented); #endif } UStreamlineDLSSGMode UStreamlineLibraryDLSSG::GetDLSSGMode() { #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("GetDLSSGMode should not be called before PostEngineInit")); return UStreamlineDLSSGMode::Off; } static const auto CVarDLSSGEnable = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streamline.DLSSG.Enable")); if (CVarDLSSGEnable != nullptr) { return DLSSGModeEnumFromIntCvar(CVarDLSSGEnable->GetInt()); } #endif return UStreamlineDLSSGMode::Off; } UStreamlineDLSSGMode UStreamlineLibraryDLSSG::GetDefaultDLSSGMode() { #if WITH_STREAMLINE if (!TryInitDLSSGLibrary()) { UE_LOG(LogStreamlineBlueprint, Error, TEXT("GetDefaultDLSSGMode should not be called before PostEngineInit")); return UStreamlineDLSSGMode::Off; } #endif if (UStreamlineLibraryDLSSG::IsDLSSGSupported()) { return UStreamlineDLSSGMode::Off; } else { return UStreamlineDLSSGMode::Off; } } #if WITH_STREAMLINE // Delayed initialization, which allows this module to be available early so blueprints can be loaded before DLSS is available in PostEngineInit bool UStreamlineLibraryDLSSG::TryInitDLSSGLibrary() { if (bDLSSGLibraryInitialized) { // TODO return true; } // Register this before we bail out so we can show error messages #if !UE_BUILD_SHIPPING if (!DLSSOnScreenMessagesDelegateHandle.IsValid()) { DLSSOnScreenMessagesDelegateHandle = FCoreDelegates::OnGetOnScreenMessages.AddStatic(&GetDLSSOnScreenMessages); } #endif if (IsStreamlineSupported()) { if (GetPlatformStreamlineRHI()->IsDLSSGSupportedByRHI()) { DLSSGSupport = ToUStreamlineFeatureSupport(QueryStreamlineDLSSGSupport()); } else { DLSSGSupport = UStreamlineFeatureSupport::NotSupportedByRHI; } } else { if (GetPlatformStreamlineSupport() == EStreamlineSupport::NotSupportedIncompatibleRHI) { DLSSGSupport = UStreamlineFeatureSupport::NotSupportedByRHI; } else { DLSSGSupport = UStreamlineFeatureSupport::NotSupported; } } bDLSSGLibraryInitialized = true; return true; } #endif // WITH_STREAMLINE void UStreamlineLibraryDLSSG::Startup() { #if WITH_STREAMLINE // This initialization will likely not succeed unless this module has been moved to PostEngineInit, and that's ok UStreamlineLibraryDLSSG::TryInitDLSSGLibrary(); UStreamlineLibrary::RegisterFeatureSupport(UStreamlineFeature::DLSSG, UStreamlineLibraryDLSSG::QueryDLSSGSupport()); #else UE_LOG(LogStreamlineBlueprint, Log, TEXT("Streamline is not supported on this platform at build time. The Streamline Blueprint library however is supported and stubbed out to ignore any calls to enable DLSS-G and will always return UStreamlineDLSSGSupport::NotSupportedByPlatformAtBuildTime, regardless of the underlying hardware. This can be used to e.g. to turn off DLSS-G related UI elements.")); UStreamlineLibraryDLSSG::DLSSGSupport = UStreamlineFeatureSupport::NotSupportedByPlatformAtBuildTime; #endif } void UStreamlineLibraryDLSSG::Shutdown() { #if WITH_STREAMLINE && !UE_BUILD_SHIPPING if (UStreamlineLibraryDLSSG::DLSSOnScreenMessagesDelegateHandle.IsValid()) { FCoreDelegates::OnGetOnScreenMessages.Remove(UStreamlineLibraryDLSSG::DLSSOnScreenMessagesDelegateHandle); UStreamlineLibraryDLSSG::DLSSOnScreenMessagesDelegateHandle.Reset(); } #endif } #undef LOCTEXT_NAMESPACE