219 lines
8.4 KiB
C++
219 lines
8.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
MetalCommandList.cpp: Metal command buffer list wrapper.
|
|
=============================================================================*/
|
|
|
|
#include "MetalCommandList.h"
|
|
#include "MetalRHIPrivate.h"
|
|
#include "MetalShaderTypes.h"
|
|
#include "MetalGraphicsPipelineState.h"
|
|
#include "MetalCommandQueue.h"
|
|
#include "MetalProfiler.h"
|
|
#include "MetalCommandBuffer.h"
|
|
|
|
#pragma mark - Public C++ Boilerplate -
|
|
|
|
#if PLATFORM_IOS
|
|
extern bool GIsSuspended;
|
|
#endif
|
|
|
|
FMetalCommandList::FMetalCommandList(FMetalCommandQueue& InCommandQueue)
|
|
: CommandQueue(InCommandQueue)
|
|
{}
|
|
|
|
FMetalCommandList::~FMetalCommandList(void)
|
|
{
|
|
}
|
|
|
|
#pragma mark - Public Command List Mutators -
|
|
|
|
static const TCHAR* StringFromCommandEncoderError(MTL::CommandEncoderErrorState ErrorState)
|
|
{
|
|
switch (ErrorState)
|
|
{
|
|
case MTL::CommandEncoderErrorStateUnknown: return TEXT("Unknown");
|
|
case MTL::CommandEncoderErrorStateAffected: return TEXT("Affected");
|
|
case MTL::CommandEncoderErrorStateCompleted: return TEXT("Completed");
|
|
case MTL::CommandEncoderErrorStateFaulted: return TEXT("Faulted");
|
|
case MTL::CommandEncoderErrorStatePending: return TEXT("Pending");
|
|
}
|
|
return TEXT("Unknown");
|
|
}
|
|
|
|
extern CORE_API bool GIsGPUCrashed;
|
|
static void ReportMetalCommandBufferFailure(MTL::CommandBuffer* CompletedBuffer, TCHAR const* ErrorType, bool bDoCheck=true)
|
|
{
|
|
GIsGPUCrashed = true;
|
|
|
|
NS::String* Label = CompletedBuffer->label();
|
|
int32 Code = CompletedBuffer->error()->code();
|
|
NS::String* Domain = CompletedBuffer->error()->domain();
|
|
NS::String* ErrorDesc = CompletedBuffer->error()->localizedDescription();
|
|
NS::String* FailureDesc = CompletedBuffer->error()->localizedFailureReason();
|
|
NS::String* RecoveryDesc = CompletedBuffer->error()->localizedRecoverySuggestion();
|
|
|
|
FString LabelString = Label ? FString(Label->cString(NS::UTF8StringEncoding)) : FString(TEXT("Unknown"));
|
|
FString DomainString = Domain ? FString(Domain->cString(NS::UTF8StringEncoding)) : FString(TEXT("Unknown"));
|
|
FString ErrorString = ErrorDesc ? FString(ErrorDesc->cString(NS::UTF8StringEncoding)) : FString(TEXT("Unknown"));
|
|
FString FailureString = FailureDesc ? FString(FailureDesc->cString(NS::UTF8StringEncoding)) : FString(TEXT("Unknown"));
|
|
FString RecoveryString = RecoveryDesc ? FString(RecoveryDesc->cString(NS::UTF8StringEncoding)) : FString(TEXT("Unknown"));
|
|
|
|
NS::String* Desc = CompletedBuffer->debugDescription();
|
|
UE_LOG(LogMetal, Warning, TEXT("Metal Command Buffer Failure: %s, %s"), ErrorType, *FString(Desc->cString(NS::UTF8StringEncoding)));
|
|
|
|
#if PLATFORM_IOS
|
|
if (bDoCheck && !GIsSuspended)
|
|
#endif
|
|
{
|
|
// Dump GPU fault information for the GPU encoders
|
|
if (&MTLCommandBufferEncoderInfoErrorKey != nullptr)
|
|
{
|
|
NS::Dictionary* ErrorDict = CompletedBuffer->error()->userInfo();
|
|
NS::Array* EncoderInfoArray = (NS::Array*)ErrorDict->object(MTL::CommandBufferEncoderInfoErrorKey);
|
|
if (EncoderInfoArray)
|
|
{
|
|
UE_LOG(LogMetal, Warning, TEXT("GPU Encoder Crash Info:"));
|
|
for(uint32 Idx = 0; Idx < EncoderInfoArray->count(); ++Idx)
|
|
{
|
|
MTL::CommandBufferEncoderInfo* EncoderInfo = (MTL::CommandBufferEncoderInfo*)EncoderInfoArray->object(Idx);
|
|
UE_LOG(LogMetal, Warning, TEXT("MTLCommandBufferEncoder - Label: %s, State: %s"), *NSStringToFString(EncoderInfo->label()), StringFromCommandEncoderError(EncoderInfo->errorState()));
|
|
NS::Array* SignPosts = EncoderInfo->debugSignposts();
|
|
if (SignPosts->count() > 0)
|
|
{
|
|
UE_LOG(LogMetal, Warning, TEXT(" Signposts:"));
|
|
for (uint32_t SignPostIdx = 0; SignPostIdx < SignPosts->count(); ++SignPostIdx)
|
|
{
|
|
NS::String* Signpost = (NS::String*)SignPosts->object(SignPostIdx);
|
|
UE_LOG(LogMetal, Warning, TEXT(" - %s"), *NSStringToFString(Signpost));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if PLATFORM_IOS
|
|
UE_LOG(LogMetal, Warning, TEXT("Command Buffer %s Failed with %s Error! Error Domain: %s Code: %d Description %s %s %s"), *LabelString, ErrorType, *DomainString, Code, *ErrorString, *FailureString, *RecoveryString);
|
|
FIOSPlatformMisc::GPUAssert();
|
|
#else
|
|
UE_LOG(LogMetal, Fatal, TEXT("Command Buffer %s Failed with %s Error! Error Domain: %s Code: %d Description %s %s %s"), *LabelString, ErrorType, *DomainString, Code, *ErrorString, *FailureString, *RecoveryString);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailureInternal(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("Internal"));
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailureTimeout(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("Timeout"), PLATFORM_IOS);
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailurePageFault(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("PageFault"));
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailureAccessRevoked(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("AccessRevoked"));
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailureNotPermitted(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
// when iOS goes into the background, it can get a delayed NotPermitted error, so we can't crash in this case, just allow it to not be submitted
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("NotPermitted"), !PLATFORM_IOS);
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailureOutOfMemory(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("OutOfMemory"));
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void MetalCommandBufferFailureInvalidResource(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("InvalidResource"));
|
|
}
|
|
|
|
static void HandleMetalCommandBufferError(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
MTL::CommandBufferError Code = (MTL::CommandBufferError)CompletedBuffer->error()->code();
|
|
switch(Code)
|
|
{
|
|
case MTL::CommandBufferErrorInternal:
|
|
MetalCommandBufferFailureInternal(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorTimeout:
|
|
MetalCommandBufferFailureTimeout(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorPageFault:
|
|
MetalCommandBufferFailurePageFault(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorAccessRevoked:
|
|
MetalCommandBufferFailureAccessRevoked(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorNotPermitted:
|
|
MetalCommandBufferFailureNotPermitted(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorOutOfMemory:
|
|
MetalCommandBufferFailureOutOfMemory(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorInvalidResource:
|
|
MetalCommandBufferFailureInvalidResource(CompletedBuffer);
|
|
break;
|
|
case MTL::CommandBufferErrorNone:
|
|
// No error
|
|
break;
|
|
default:
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("Unknown"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void HandleAMDMetalCommandBufferError(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
HandleMetalCommandBufferError(CompletedBuffer);
|
|
}
|
|
|
|
static __attribute__ ((optnone)) void HandleIntelMetalCommandBufferError(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
HandleMetalCommandBufferError(CompletedBuffer);
|
|
}
|
|
|
|
void FMetalCommandList::HandleMetalCommandBufferFailure(MTL::CommandBuffer* CompletedBuffer)
|
|
{
|
|
if (CompletedBuffer->error()->domain()->isEqualToString(NS::String::string("MTLCommandBufferErrorDomain", NS::UTF8StringEncoding)))
|
|
{
|
|
if (GRHIVendorId && IsRHIDeviceAMD())
|
|
{
|
|
HandleAMDMetalCommandBufferError(CompletedBuffer);
|
|
}
|
|
else if (GRHIVendorId && IsRHIDeviceIntel())
|
|
{
|
|
HandleIntelMetalCommandBufferError(CompletedBuffer);
|
|
}
|
|
else
|
|
{
|
|
HandleMetalCommandBufferError(CompletedBuffer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReportMetalCommandBufferFailure(CompletedBuffer, TEXT("Unknown"));
|
|
}
|
|
}
|
|
|
|
void FMetalCommandList::FinalizeCommandBuffer(FMetalCommandBuffer* Buffer)
|
|
{
|
|
#if RHI_NEW_GPU_PROFILER == 0
|
|
check(Buffer);
|
|
|
|
FMetalCommandBufferTimer& Timer = FMetalCommandBufferTimer::GetFrameBufferTimer();
|
|
Timer.Submit();
|
|
|
|
Buffer->SetTimer(&Timer);
|
|
#endif
|
|
}
|