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

299 lines
8.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
MetalRHIPrivate.h: Private Metal RHI definitions.
=============================================================================*/
#pragma once
#include "MetalRHI.h"
#include "MetalThirdParty.h"
#include "PixelFormat.h"
#include "RHICommandList.h"
#include "RHIGlobals.h"
class FMetalDevice;
class FMetalSurface;
DECLARE_LOG_CATEGORY_EXTERN(LogMetal, Display, All);
DECLARE_DELEGATE_OneParam(FMetalCommandBufferCompletionHandler, MTL::CommandBuffer*);
// Whether the Metal RHI is initialized sufficiently to handle resources
extern bool GIsMetalInitialized;
// Requirement for vertex buffer offset field
#if PLATFORM_MAC
const uint32 BufferOffsetAlignment = 256;
const uint32 BufferBackedLinearTextureOffsetAlignment = 1024;
#else
const uint32 BufferOffsetAlignment = 16;
const uint32 BufferBackedLinearTextureOffsetAlignment = 64;
#endif
// The maximum buffer page size that can be uploaded in a set*Bytes call
const uint32 MetalBufferPageSize = 4096;
// The buffer size that is more efficiently uploaded in a set*Bytes call - defined in terms of BufferOffsetAlignment
#if PLATFORM_MAC
const uint32 MetalBufferBytesSize = BufferOffsetAlignment * 2;
#else
const uint32 MetalBufferBytesSize = BufferOffsetAlignment * 32;
#endif
#define METAL_USE_METAL_SHADER_CONVERTER PLATFORM_SUPPORTS_BINDLESS_RENDERING
// Metal Shader Converter
#if METAL_USE_METAL_SHADER_CONVERTER
THIRD_PARTY_INCLUDES_START
#include "metal_irconverter.h"
#define IR_RUNTIME_METALCPP 1
#define IR_PRIVATE_IMPLEMENTATION 1
#include "metal_irconverter_runtime.h"
THIRD_PARTY_INCLUDES_END
constexpr uint64_t kIRStandardHeapBindPoint = 0;
#endif
#define BUFFER_CACHE_MODE MTL::ResourceCPUCacheModeDefaultCache
#if PLATFORM_MAC
#define BUFFER_MANAGED_MEM MTL::ResourceStorageModeManaged
#define BUFFER_STORAGE_MODE MTL::StorageModeShared
#define BUFFER_RESOURCE_STORAGE_MANAGED MTL::ResourceStorageModeManaged
#define BUFFER_DYNAMIC_REALLOC BUF_AnyDynamic
// How many possible vertex streams are allowed
const uint32 MaxMetalStreams = 31;
#else
#define BUFFER_MANAGED_MEM 0
#if WITH_IOS_SIMULATOR
#define BUFFER_STORAGE_MODE MTL::StorageModePrivate
#define BUFFER_RESOURCE_STORAGE_MANAGED MTL::ResourceStorageModePrivate
#else
#define BUFFER_STORAGE_MODE MTL::StorageModeShared
#define BUFFER_RESOURCE_STORAGE_MANAGED MTL::ResourceStorageModeShared
#endif
#define BUFFER_DYNAMIC_REALLOC BUF_AnyDynamic
// How many possible vertex streams are allowed
const uint32 MaxMetalStreams = 30;
#endif
// Unavailable on iOS, but dealing with this clutters the code.
enum EMTLTextureType
{
EMTLTextureTypeCubeArray = 6
};
// This is the right VERSION check, see Availability.h in the SDK
#define METAL_SUPPORTS_INDIRECT_ARGUMENT_BUFFERS 1
#define METAL_SUPPORTS_CAPTURE_MANAGER 1
#define METAL_SUPPORTS_TILE_SHADERS 1
// In addition to compile-time SDK checks we also need a way to check if these are available on runtime
extern bool GMetalSupportsCaptureManager;
struct FMetalBufferFormat
{
// Valid linear texture pixel formats - potentially different than the actual texture formats
MTL::PixelFormat LinearTextureFormat;
// Metal buffer data types for manual ALU format conversions
uint8 DataFormat;
};
extern FMetalBufferFormat GMetalBufferFormats[PF_MAX];
#define METAL_DEBUG_OPTIONS !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
#if METAL_DEBUG_OPTIONS
#define METAL_DEBUG_OPTION(Code) Code
#else
#define METAL_DEBUG_OPTION(Code)
#endif
extern bool GMetalCommandBufferDebuggingEnabled;
/** Set to 1 to enable GPU events in Xcode frame debugger */
#ifndef ENABLE_METAL_GPUEVENTS_IN_TEST
#define ENABLE_METAL_GPUEVENTS_IN_TEST 0
#endif
#define ENABLE_METAL_GPUEVENTS (UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT || (UE_BUILD_TEST && ENABLE_METAL_GPUEVENTS_IN_TEST))
#define ENABLE_METAL_GPUPROFILE (ENABLE_METAL_GPUEVENTS && 1)
#if ENABLE_METAL_GPUPROFILE && RHI_NEW_GPU_PROFILER == 0
#define METAL_GPUPROFILE(Code) Code
#else
#define METAL_GPUPROFILE(Code)
#endif
#define UNREAL_TO_METAL_BUFFER_INDEX(Index) ((MaxMetalStreams - 1) - Index)
#define METAL_TO_UNREAL_BUFFER_INDEX(Index) ((MaxMetalStreams - 1) - Index)
#define METAL_NEW_NONNULL_DECL (__clang_major__ >= 9)
#if PLATFORM_IOS
#define METAL_FATAL_ERROR(Format, ...) { UE_LOG(LogMetal, Warning, Format, __VA_ARGS__); FIOSPlatformMisc::MetalAssert(); }
#else
#define METAL_FATAL_ERROR(Format, ...) UE_LOG(LogMetal, Fatal, Format, __VA_ARGS__)
#endif
#define METAL_FATAL_ASSERT(Condition, Format, ...) if (!(Condition)) { METAL_FATAL_ERROR(Format, __VA_ARGS__); }
#if !defined(METAL_IGNORED)
#define METAL_IGNORED(Func)
#endif
FORCEINLINE bool IsMetalBindlessEnabled()
{
return GRHIGlobals.bSupportsBindless && GMaxRHIFeatureLevel >= ERHIFeatureLevel::SM6;
}
// Access the underlying surface object from any kind of texture
FMetalSurface* GetMetalSurfaceFromRHITexture(FRHITexture* Texture);
#define NOT_SUPPORTED(Func) UE_LOG(LogMetal, Fatal, TEXT("'%s' is not supported"), TEXT(Func));
FORCEINLINE bool MetalIsSafeToUseRHIThreadResources()
{
// we can use RHI thread resources if we are on the RHIThread or on RenderingThread when there's no RHI thread, or the RHI thread is stalled or inactive
return (GIsMetalInitialized && !GIsRHIInitialized)
|| IsInRHIThread()
|| (IsInRenderingThread() && (!IsRunningRHIInSeparateThread() || !FRHICommandListExecutor::AreRHITasksActive() || FRHICommandListImmediate::IsStalled()));
}
FORCEINLINE int32 GetMetalCubeFace(ECubeFace Face)
{
// According to Metal docs these should match now: https://developer.apple.com/library/prerelease/ios/documentation/Metal/Reference/MTLTexture_Ref/index.html#//apple_ref/c/tdef/MTLTextureType
switch (Face)
{
case CubeFace_PosX:;
default: return 0;
case CubeFace_NegX: return 1;
case CubeFace_PosY: return 2;
case CubeFace_NegY: return 3;
case CubeFace_PosZ: return 4;
case CubeFace_NegZ: return 5;
}
}
FORCEINLINE MTL::LoadAction GetMetalRTLoadAction(ERenderTargetLoadAction LoadAction)
{
switch(LoadAction)
{
case ERenderTargetLoadAction::ENoAction: return MTL::LoadActionDontCare;
case ERenderTargetLoadAction::ELoad: return MTL::LoadActionLoad;
case ERenderTargetLoadAction::EClear: return MTL::LoadActionClear;
default: return MTL::LoadActionDontCare;
}
}
MTL::PrimitiveType TranslatePrimitiveType(uint32 PrimitiveType);
#if PLATFORM_MAC
MTL::PrimitiveTopologyClass TranslatePrimitiveTopology(uint32 PrimitiveType);
#endif
MTL::PixelFormat UEToMetalFormat(FMetalDevice& Device, EPixelFormat UEFormat, bool bSRGB);
uint8 GetMetalPixelFormatKey(MTL::PixelFormat Format);
template<class T>
struct TMetalResourceTraits
{
};
template<typename TRHIType>
static FORCEINLINE typename TMetalResourceTraits<TRHIType>::TConcreteType* ResourceCast(TRHIType* Resource)
{
return static_cast<typename TMetalResourceTraits<TRHIType>::TConcreteType*>(Resource);
}
static FORCEINLINE FMetalSurface* ResourceCast(FRHITexture* Texture)
{
return GetMetalSurfaceFromRHITexture(Texture);
}
MTL::LanguageVersion ValidateVersion(uint32 Version);
// Needs to be the same as EShaderFrequency when all stages are supported, but unlike EShaderFrequency you can compile out stages.
enum EMetalShaderStages
{
Vertex,
Pixel,
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
Geometry,
#endif
#if PLATFORM_SUPPORTS_MESH_SHADERS
Mesh,
Amplification,
#endif
Compute,
Num,
};
FORCEINLINE EShaderFrequency GetRHIShaderFrequency(EMetalShaderStages Stage)
{
switch (Stage)
{
case EMetalShaderStages::Vertex:
return SF_Vertex;
case EMetalShaderStages::Pixel:
return SF_Pixel;
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
case EMetalShaderStages::Geometry:
return SF_Geometry;
#endif
#if PLATFORM_SUPPORTS_MESH_SHADERS
case EMetalShaderStages::Mesh:
return SF_Mesh;
case EMetalShaderStages::Amplification:
return SF_Amplification;
#endif
case EMetalShaderStages::Compute:
return SF_Compute;
default:
return SF_NumFrequencies;
}
}
FORCEINLINE EMetalShaderStages GetMetalShaderFrequency(EShaderFrequency Stage)
{
switch (Stage)
{
case SF_Vertex:
return EMetalShaderStages::Vertex;
case SF_Pixel:
return EMetalShaderStages::Pixel;
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
case SF_Geometry:
return EMetalShaderStages::Geometry;
#endif
#if PLATFORM_SUPPORTS_MESH_SHADERS
case SF_Mesh:
return EMetalShaderStages::Mesh;
case SF_Amplification:
return EMetalShaderStages::Amplification;
#endif
case SF_Compute:
return EMetalShaderStages::Compute;
default:
return EMetalShaderStages::Num;
}
}
FORCEINLINE FString NSStringToFString(NS::String* InputString)
{
return FString((__bridge CFStringRef)InputString);
}
FORCEINLINE NS::String* FStringToNSString(const FString& InputString)
{
return ((NS::String*)InputString.GetCFString())->autorelease();
}
// helper functions to reduce copy and pasted checks all around
bool ShouldUseMemoryless(ETextureCreateFlags Flags);
bool AllowMSAA();