Files
UnrealEngine/Engine/Source/Runtime/RHI/Private/Apple/AppleDynamicRHI.cpp
2025-05-18 13:04:45 +08:00

176 lines
5.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RHI.h"
#include "Modules/ModuleManager.h"
#include "Misc/App.h"
#include "Misc/CommandLine.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/MessageDialog.h"
#include "DataDrivenShaderPlatformInfo.h"
#include COMPILED_PLATFORM_HEADER_WITH_PREFIX(Apple/Platform, PlatformDynamicRHI.h)
#define LOCTEXT_NAMESPACE "AppleRHI"
//------------------------------------------------------------------------------
// MARK: - FAppleDynamicRHIOptions Union
//
union FAppleDynamicRHIOptions
{
struct
{
uint16 ForceSM5 : 1;
uint16 ForceSM6 : 1;
uint16 PreferES31 : 1;
uint16 ForceMTL : 1;
uint16 UnusedReservedBits : 12;
};
uint16 All;
};
//------------------------------------------------------------------------------
// MARK: - Apple Dynamic RHI Support Routines
//
static inline bool ValidateAppleDynamicRHIOptions(FAppleDynamicRHIOptions* Options)
{
if (0 != (Options->ForceSM5 & Options->ForceSM6))
{
UE_LOG(LogRHI, Fatal, TEXT("-sm5 and -sm6 are mutually exclusive options but more than one was specified on the command line."));
return false;
}
if (0 != (Options->ForceMTL & Options->ForceSM6))
{
UE_LOG(LogRHI, Warning, TEXT("-mtl and -sm6 are incompatible options, using MetalRHI with SM5."));
Options->ForceSM5 = 1;
Options->ForceSM6 = 0;
Options->ForceMTL = 1;
}
if(Options->ForceSM6)
{
bool bSupportsSM6 = false;
if (@available(macOS 15.0, *))
{
bSupportsSM6 = true;
}
if(!bSupportsSM6)
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("MetalRHIOptionsError", "-sm6 is selected but Mac requires OS 15 to support SM6"));
UE_LOG(LogRHI, Fatal, TEXT("-sm6 is selected but Mac requires OS 15 to support SM6"));
return false;
}
}
return true;
}
static bool InitAppleDynamicRHIOptions(FAppleDynamicRHIOptions* Options)
{
Options->ForceSM5 = FParse::Param(FCommandLine::Get(), TEXT("sm5"));
Options->ForceSM6 = FParse::Param(FCommandLine::Get(), TEXT("sm6"));
Options->PreferES31 = FPlatformDynamicRHI::ShouldPreferFeatureLevelES31() && !(Options->ForceSM5 || Options->ForceSM6);
Options->ForceMTL = FParse::Param(FCommandLine::Get(), TEXT("mtl"));
return ValidateAppleDynamicRHIOptions(Options);
}
static inline bool ShouldUseShaderModelPreference(const FAppleDynamicRHIOptions& Options)
{
return (0 != (Options.ForceSM5 | Options.ForceSM6 | Options.PreferES31));
}
static void ComputeRequestedFeatureLevel(const FAppleDynamicRHIOptions& Options, ERHIFeatureLevel::Type& RequestedFeatureLevel)
{
if (!ShouldUseShaderModelPreference(Options))
{
TArray<FString> TargetedShaderFormats;
FPlatformDynamicRHI::AddTargetedShaderFormats(TargetedShaderFormats);
if (TargetedShaderFormats.Num() > 0)
{
// Pick the highest targeted shader format
for(const FString& Format : TargetedShaderFormats)
{
FName ShaderFormatName(*Format);
EShaderPlatform TargetedPlatform = ShaderFormatToLegacyShaderPlatform(ShaderFormatName);
ERHIFeatureLevel::Type FeatureLevel = GetMaxSupportedFeatureLevel(TargetedPlatform);
if(FeatureLevel > RequestedFeatureLevel || RequestedFeatureLevel == ERHIFeatureLevel::Num)
{
RequestedFeatureLevel = FeatureLevel;
}
}
}
else
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("MetalMissingTargetError", "No Targeted RHI is set for this project, defaulting to SM5"));
RequestedFeatureLevel = ERHIFeatureLevel::SM5;
}
}
else
{
if (Options.ForceSM6)
{
RequestedFeatureLevel = ERHIFeatureLevel::SM6;
}
else if (Options.ForceSM5)
{
RequestedFeatureLevel = ERHIFeatureLevel::SM5;
}
else
{
check(Options.PreferES31 != 0);
RequestedFeatureLevel = ERHIFeatureLevel::ES3_1;
}
}
check(RequestedFeatureLevel != ERHIFeatureLevel::Num);
}
static IDynamicRHIModule* LoadDynamicRHIModule(ERHIFeatureLevel::Type& RequestedFeatureLevel)
{
IDynamicRHIModule* DynamicRHIModule = nullptr;
FAppleDynamicRHIOptions Options = { .All = 0 };
if (!InitAppleDynamicRHIOptions(&Options))
{
return nullptr;
}
ComputeRequestedFeatureLevel(Options, RequestedFeatureLevel);
DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("MetalRHI"));
return DynamicRHIModule;
}
//------------------------------------------------------------------------------
// MARK: - Dynamic RHI API
//
FDynamicRHI* PlatformCreateDynamicRHI()
{
FDynamicRHI* DynamicRHI = nullptr;
IDynamicRHIModule* DynamicRHIModule = nullptr;
ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num;
if (nullptr != (DynamicRHIModule = LoadDynamicRHIModule(RequestedFeatureLevel)))
{
DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
// The Feature Level is known after creating the RHI
FString FeatureLevelName;
GetFeatureLevelName(GMaxRHIFeatureLevel, FeatureLevelName);
FApp::SetGraphicsRHI(FString::Printf(TEXT("Metal (%s)"), *FeatureLevelName));
}
return DynamicRHI;
}
#undef LOCTEXT_NAMESPACE