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

256 lines
9.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
MetalDynamicRHI_Shaders.cpp: Metal Dynamic RHI Class Shader Methods.
=============================================================================*/
#include "MetalDynamicRHI.h"
#include "MetalRHIPrivate.h"
#include "MetalShaderTypes.h"
#include "Shaders/MetalShaderLibrary.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "Serialization/StaticMemoryReader.h"
#include "Interfaces/IPluginManager.h"
//------------------------------------------------------------------------------
#pragma mark - Metal Dynamic RHI Shader Methods
FVertexShaderRHIRef FMetalDynamicRHI::RHICreateVertexShader(TArrayView<const uint8> Code, const FSHAHash& Hash)
{
MTL_SCOPED_AUTORELEASE_POOL;
FMetalVertexShader* Shader = new FMetalVertexShader(*Device, Code);
return Shader;
}
FPixelShaderRHIRef FMetalDynamicRHI::RHICreatePixelShader(TArrayView<const uint8> Code, const FSHAHash& Hash)
{
MTL_SCOPED_AUTORELEASE_POOL;
FMetalPixelShader* Shader = new FMetalPixelShader(*Device, Code);
return Shader;
}
FGeometryShaderRHIRef FMetalDynamicRHI::RHICreateGeometryShader(TArrayView<const uint8> Code, const FSHAHash& Hash)
{
MTL_SCOPED_AUTORELEASE_POOL;
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
FMetalGeometryShader* Shader = new FMetalGeometryShader(*Device, Code);
return Shader;
#else
return nullptr;
#endif
}
FComputeShaderRHIRef FMetalDynamicRHI::RHICreateComputeShader(TArrayView<const uint8> Code, const FSHAHash& Hash)
{
MTL_SCOPED_AUTORELEASE_POOL;
return new FMetalComputeShader(*Device, Code, MTLLibraryPtr());
}
#if PLATFORM_SUPPORTS_MESH_SHADERS
FMeshShaderRHIRef FMetalDynamicRHI::RHICreateMeshShader(TArrayView<const uint8> Code, const FSHAHash& Hash)
{
MTL_SCOPED_AUTORELEASE_POOL;
return new FMetalMeshShader(*Device, Code);
}
FAmplificationShaderRHIRef FMetalDynamicRHI::RHICreateAmplificationShader(TArrayView<const uint8> Code, const FSHAHash& Hash)
{
MTL_SCOPED_AUTORELEASE_POOL;
return new FMetalAmplificationShader(*Device, Code);
}
#endif
#if METAL_RHI_RAYTRACING
FRayTracingShaderRHIRef FMetalDynamicRHI::RHICreateRayTracingShader(TArrayView<const uint8> Code, const FSHAHash& Hash, EShaderFrequency ShaderFrequency)
{
switch (ShaderFrequency)
{
default: checkNoEntry(); return nullptr;
}
}
#endif // METAL_RHI_RAYTRACING
FRHIShaderLibraryRef FMetalDynamicRHI::RHICreateShaderLibrary(EShaderPlatform Platform, FString const& FilePath, FString const& Name)
{
FString METAL_MAP_EXTENSION(TEXT(".metalmap"));
MTL_SCOPED_AUTORELEASE_POOL;
FRHIShaderLibraryRef Result = nullptr;
const FName PlatformName = FDataDrivenShaderPlatformInfo::GetName(Platform);
const FName ShaderFormatName = LegacyShaderPlatformToShaderFormat(Platform);
FString ShaderFormatAndPlatform = ShaderFormatName.ToString() + TEXT("-") + PlatformName.ToString();
FString LibName = FString::Printf(TEXT("%s_%s"), *Name, *ShaderFormatAndPlatform);
LibName.ToLowerInline();
FString BinaryShaderFile = FilePath / LibName + METAL_MAP_EXTENSION;
if (IFileManager::Get().FileExists(*BinaryShaderFile) == false)
{
// the metal map files are stored in UFS file system
// for pak files this means they might be stored in a different location as the pak files will mount them to the project content directory
// the metal libraries are stores non UFS and could be anywhere on the file system.
// if we don't find the metalmap file straight away try the pak file path
BinaryShaderFile = FPaths::ProjectContentDir() / LibName + METAL_MAP_EXTENSION;
if (IFileManager::Get().FileExists(*BinaryShaderFile) == false)
{
// See if its in a Plugin
const TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(*Name);
if(Plugin != nullptr)
{
BinaryShaderFile = FPaths::Combine(Plugin->GetContentDir(), LibName + METAL_MAP_EXTENSION);
}
else
{
// GFP might not be loaded yet
BinaryShaderFile = FPaths::ProjectPluginsDir() / TEXT("GameFeatures") / Name / TEXT("Content") / LibName + METAL_MAP_EXTENSION;
}
}
}
FScopeLock Lock(&FMetalShaderLibrary::LoadedShaderLibraryMutex);
FRHIShaderLibrary** FoundShaderLibrary = FMetalShaderLibrary::LoadedShaderLibraryMap.Find(BinaryShaderFile);
if (FoundShaderLibrary)
{
return *FoundShaderLibrary;
}
auto SerializeShaderCode = [](FMetalShaderLibrary::FShaderCodeArrayType& Array, FArchive& Ar)
{
#if !USE_MMAPPED_SHADERARCHIVE
Ar << Array;
#else
if (Ar.GetArchiveName() == TEXT("FStaticMemoryReader"))
{
using ArrayType = std::remove_cvref_t <decltype(Array)>;
FStaticMemoryReader* MemReader = static_cast<FStaticMemoryReader*>(&Ar);
typename ArrayType::SizeType SerializeNum;
Ar << SerializeNum;
uint64 ArrayBytes = SerializeNum * sizeof(typename ArrayType::ElementType);
uint64 Offset = Ar.Tell();
Array = FMetalShaderLibrary::FShaderCodeArrayType(MemReader->GetData() + Offset, SerializeNum);
Ar.Seek(Offset + ArrayBytes);
}
else
{
UE_LOG(LogMetal, Fatal, TEXT("mmapped array must be serialized via FStaticMemoryReader"));
}
#endif
};
auto MapShaderLibraryFile = [](const FString& FilePath, FMetalShaderLibrary::FShaderLibDataOwner& MemOwner)
{
if (FPlatformProperties::SupportsMemoryMappedFiles())
{
FOpenMappedResult Res = FPlatformFileManager::Get().GetPlatformFile().OpenMappedEx(*FilePath);
MemOwner.MappedCacheFile = Res.HasError() ? nullptr : Res.StealValue();
if (MemOwner.MappedCacheFile.IsValid())
{
MemOwner.MappedRegion = TUniquePtr<IMappedFileRegion>(MemOwner.MappedCacheFile->MapRegion(0, MemOwner.MappedCacheFile->GetFileSize(), EMappedFileFlags::ENone));
}
}
};
#if !USE_MMAPPED_SHADERARCHIVE
FArchive* BinaryShaderAr = IFileManager::Get().CreateFileReader(*BinaryShaderFile);
#else
FStaticMemoryReader* BinaryShaderAr = nullptr;
TUniquePtr<FMetalShaderLibrary::FShaderLibDataOwner> MemOwner = MakeUnique<FMetalShaderLibrary::FShaderLibDataOwner>();
MapShaderLibraryFile(BinaryShaderFile, *MemOwner);
if (MemOwner->MappedRegion.IsValid())
{
UE_LOG(LogMetal, Display, TEXT("mmapping %s, %d bytes"), *BinaryShaderFile, MemOwner->MappedCacheFile->GetFileSize());
BinaryShaderAr = new FStaticMemoryReader(MemOwner->MappedRegion->GetMappedPtr(), MemOwner->MappedCacheFile->GetFileSize());
}
if(BinaryShaderAr == nullptr)
{
TArray<uint8>& FileData = MemOwner->Mem;
if (FFileHelper::LoadFileToArray(FileData, *BinaryShaderFile))
{
UE_LOG(LogMetal, Display, TEXT("emulating mmapping %s, %d bytes!"), *BinaryShaderFile, FileData.Num());
BinaryShaderAr = new FStaticMemoryReader(FileData.GetData(), FileData.Num());
}
}
#endif
if( BinaryShaderAr != NULL )
{
FMetalShaderLibraryHeader Header;
FSerializedShaderArchive SerializedShaders;
FMetalShaderLibrary::FShaderCodeArrayType ShaderCode;
*BinaryShaderAr << Header;
*BinaryShaderAr << SerializedShaders;
SerializeShaderCode(ShaderCode,*BinaryShaderAr);
BinaryShaderAr->Flush();
delete BinaryShaderAr;
// Would be good to check the language version of the library with the archive format here.
if (Header.Format == ShaderFormatName.GetPlainNameString())
{
check(((SerializedShaders.GetNumShaders() + Header.NumShadersPerLibrary - 1) / Header.NumShadersPerLibrary) == Header.NumLibraries);
TArray<TUniquePtr<FMetalShaderLibrary::FLazyMetalLib>> LazyLibraries;
LazyLibraries.Empty(Header.NumLibraries);
for (uint32 i = 0; i < Header.NumLibraries; i++)
{
FString MetalLibraryFilePath = (FilePath / LibName) + FString::Printf(TEXT(".%d.metallib"), i);
TUniquePtr<FMetalShaderLibrary::FLazyMetalLib> LazyLibrary = MakeUnique<FMetalShaderLibrary::FLazyMetalLib>();
LazyLibrary->MetalLibraryFilePath = MetalLibraryFilePath;
LazyLibrary->Data = MakeUnique<FMetalShaderLibrary::FShaderLibDataOwner>();
MapShaderLibraryFile(MetalLibraryFilePath, *LazyLibrary->Data);
LazyLibraries.Add(MoveTemp(LazyLibrary));
}
FMetalShaderLibrary* MtlLib = new FMetalShaderLibrary(*Device, Platform, Name, BinaryShaderFile, Header, MoveTemp(SerializedShaders), MoveTemp(ShaderCode), MoveTemp(LazyLibraries)
#if USE_MMAPPED_SHADERARCHIVE
, MoveTemp(MemOwner)
#endif
);
Result = MtlLib;
FMetalShaderLibrary::LoadedShaderLibraryMap.Add(BinaryShaderFile, Result.GetReference());
}
else
{
UE_LOG(LogMetal, Display, TEXT("Unknown shader format for %s!"), *LibName);
}
}
else
{
UE_LOG(LogMetal, Display, TEXT("No .metalmap file found for %s!"), *LibName);
}
return Result;
}
FBoundShaderStateRHIRef FMetalDynamicRHI::RHICreateBoundShaderState(
FRHIVertexDeclaration* VertexDeclarationRHI,
FRHIVertexShader* VertexShaderRHI,
FRHIPixelShader* PixelShaderRHI,
FRHIGeometryShader* GeometryShaderRHI)
{
NOT_SUPPORTED("RHICreateBoundShaderState");
return nullptr;
}
FRHIShaderLibraryRef FMetalDynamicRHI::RHICreateShaderLibrary_RenderThread(class FRHICommandListImmediate& RHICmdList, EShaderPlatform Platform, FString FilePath, FString Name)
{
return RHICreateShaderLibrary(Platform, FilePath, Name);
}