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

236 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MetalCounterSampler.h"
#include "MetalDynamicRHI.h"
static TAutoConsoleVariable<bool> CVarMetalRHIInsertCounterSampleBarrier(
TEXT("rhi.Metal.InsertCounterSampleBarrier"),
true,
TEXT("Whether to insert a counter sampler barrier to provide the most accurate timings. (default: true)\n"),
ECVF_ReadOnly);
void FMetalCounterSample::ResolveStageCounters(uint64_t& InStartTime, uint64_t& InEndTime)
{
if(!bResolved)
{
NS::Data* Data;
if(SampleType == EMetalCounterSampleType::RenderStage)
{
NS::Range Range = NS::Range::Make(Offset, 4);
Data = SampleBuffer->resolveCounterRange(Range);
}
else
{
NS::Range Range = NS::Range::Make(Offset, 2);
Data = SampleBuffer->resolveCounterRange(Range);
}
// Convert the contents of the counter sample buffer into the standard data format.
check(Data);
if (Data == nullptr)
{
return;
}
MTL::CounterResultTimestamp* Timestamps = (MTL::CounterResultTimestamp *)(Data->mutableBytes());
if(SampleType == EMetalCounterSampleType::RenderStage)
{
uint64_t StartTimeVertex = Timestamps[0].timestamp;
uint64_t EndTimeVertex = Timestamps[1].timestamp;
uint64_t StartTimeFragment = Timestamps[2].timestamp;
uint64_t EndTimeFragment = Timestamps[3].timestamp;
StartTime = StartTimeVertex;
EndTime = EndTimeFragment;
}
else
{
StartTime = Timestamps[0].timestamp;
EndTime = Timestamps[1].timestamp;
}
double OneOverNanosToCycles = 1.0 / 1000000000.0 / FPlatformTime::GetSecondsPerCycle64();
StartTime = FMath::TruncToInt64(StartTime * OneOverNanosToCycles);
EndTime = FMath::TruncToInt64(EndTime * OneOverNanosToCycles);
bResolved = true;
}
InStartTime = StartTime;
InEndTime = EndTime;
}
void FMetalCounterSample::ResolveBoundaryCounter(uint64_t& Time)
{
if(!bResolved)
{
NS::Range Range = NS::Range::Make(Offset, 1);
// Convert the contents of the counter sample buffer into the standard data format.
NS::Data* Data = SampleBuffer->resolveCounterRange(Range);
check(Data);
if (Data == nullptr)
{
return;
}
MTL::CounterResultTimestamp* Timestamps = (MTL::CounterResultTimestamp *)(Data->mutableBytes());
EndTime = Timestamps->timestamp;
bResolved = true;
}
Time = EndTime;
}
TLockFreePointerListUnordered<void, PLATFORM_CACHE_LINE_SIZE> FMetalCounterSample::MemoryPool;
FMetalCounterSampler::FMetalCounterSampler(FMetalDevice* InDevice, uint32_t SampleCount) : Device(InDevice)
{
Size = SampleCount;
}
FMetalCounterSampler::~FMetalCounterSampler()
{
SampleBuffer->release();
}
MTL::CounterSampleBuffer* FMetalCounterSampler::SwapOrAllocateBuffer(uint32_t SampleSize, uint32_t& OutOffset)
{
FScopeLock Lock(&Mutex);
OutOffset = Offset;
Offset += SampleSize;
if(Offset > Size || !SampleBuffer)
{
// Re-add Counter Sampler on next frame
if(SampleBuffer)
{
FMetalDynamicRHI::Get().DeferredDelete([this, InSampleBuffer=SampleBuffer](){
SampleBufferFreePool.Add(InSampleBuffer);
});
}
if(SampleBufferFreePool.Num() > 0)
{
SampleBuffer = SampleBufferFreePool.Pop();
Offset = 0;
OutOffset = 0;
return SampleBuffer;
}
MTL::CounterSampleBufferDescriptor* BufferDesc;
BufferDesc = MTL::CounterSampleBufferDescriptor::alloc()->init();
NS::Array* CounterSets = Device->GetDevice()->counterSets();
const MTL::CounterSet* CounterSet = (MTL::CounterSet*)CounterSets->object(0);
BufferDesc->setCounterSet(CounterSet);
BufferDesc->setStorageMode(MTL::StorageModeShared);
Offset = 0;
OutOffset = 0;
BufferDesc->setSampleCount(Size);
NS::Error* DeviceError;
SampleBuffer = Device->GetDevice()->newCounterSampleBuffer(BufferDesc, &DeviceError);
check(SampleBuffer);
BufferDesc->release();
}
check(Offset <= Size);
return SampleBuffer;
}
FMetalCounterSamplePtr FMetalCounterSampler::SetupStageCounters(MTL::ComputePassDescriptor* ComputePassDesc)
{
uint32_t OutOffset;
MTL::CounterSampleBuffer* Buffer = SwapOrAllocateBuffer(2, OutOffset);
check(Buffer);
FMetalCounterSamplePtr Sample = FMetalCounterSamplePtr(new FMetalCounterSample(EMetalCounterSampleType::ComputeStage, Buffer, OutOffset));
MTL::ComputePassSampleBufferAttachmentDescriptor* SampleDesc = ComputePassDesc->sampleBufferAttachments()->object(0);
SampleDesc->setSampleBuffer(Buffer);
SampleDesc->setStartOfEncoderSampleIndex(OutOffset);
SampleDesc->setEndOfEncoderSampleIndex(OutOffset+1);
return Sample;
}
FMetalCounterSamplePtr FMetalCounterSampler::SetupStageCounters(MTL::BlitPassDescriptor* BlitPassDesc)
{
uint32_t OutOffset;
MTL::CounterSampleBuffer* Buffer = SwapOrAllocateBuffer(2, OutOffset);
check(Buffer);
FMetalCounterSamplePtr Sample = FMetalCounterSamplePtr(new FMetalCounterSample(EMetalCounterSampleType::BlitStage, Buffer, OutOffset));
MTL::BlitPassSampleBufferAttachmentDescriptor* SampleDesc = BlitPassDesc->sampleBufferAttachments()->object(0);
SampleDesc->setSampleBuffer(Buffer);
SampleDesc->setStartOfEncoderSampleIndex(OutOffset);
SampleDesc->setEndOfEncoderSampleIndex(OutOffset+1);
return Sample;
}
FMetalCounterSamplePtr FMetalCounterSampler::SetupStageCounters(MTL::RenderPassDescriptor* InRenderPassDesc)
{
uint32_t OutOffset;
MTL::CounterSampleBuffer* Buffer = SwapOrAllocateBuffer(4, OutOffset);
check(Buffer);
FMetalCounterSamplePtr Sample = FMetalCounterSamplePtr(new FMetalCounterSample(EMetalCounterSampleType::RenderStage, Buffer, OutOffset));
MTL::RenderPassSampleBufferAttachmentDescriptor* SampleDesc = InRenderPassDesc->sampleBufferAttachments()->object(0);
SampleDesc->setSampleBuffer(Buffer);
SampleDesc->setStartOfVertexSampleIndex(OutOffset);
SampleDesc->setEndOfVertexSampleIndex(OutOffset+1);
SampleDesc->setStartOfFragmentSampleIndex(OutOffset+2);
SampleDesc->setEndOfFragmentSampleIndex(OutOffset+3);
return Sample;
}
FMetalCounterSamplePtr FMetalCounterSampler::SetupBoundaryCounters(MTL::RenderCommandEncoder* RenderCommandEncoder)
{
uint32_t OutOffset;
MTL::CounterSampleBuffer* Buffer = SwapOrAllocateBuffer(1, OutOffset);
check(Buffer);
FMetalCounterSamplePtr Sample = FMetalCounterSamplePtr(new FMetalCounterSample(EMetalCounterSampleType::DrawBoundary, Buffer, OutOffset));
RenderCommandEncoder->sampleCountersInBuffer(Buffer, OutOffset++, CVarMetalRHIInsertCounterSampleBarrier.GetValueOnAnyThread());
return Sample;
}
FMetalCounterSamplePtr FMetalCounterSampler::SetupBoundaryCounters(MTL::ComputeCommandEncoder* ComputeCommandEncoder)
{
uint32_t OutOffset;
MTL::CounterSampleBuffer* Buffer = SwapOrAllocateBuffer(1, OutOffset);
check(Buffer);
FMetalCounterSamplePtr Sample = FMetalCounterSamplePtr(new FMetalCounterSample(EMetalCounterSampleType::DispatchBoundary, Buffer, OutOffset));
ComputeCommandEncoder->sampleCountersInBuffer(Buffer, OutOffset++, CVarMetalRHIInsertCounterSampleBarrier.GetValueOnAnyThread());
return Sample;
}
FMetalCounterSamplePtr FMetalCounterSampler::SetupBoundaryCounters(MTL::BlitCommandEncoder* BlitCommandEncoder)
{
uint32_t OutOffset;
MTL::CounterSampleBuffer* Buffer = SwapOrAllocateBuffer(1, OutOffset);
check(Buffer);
FMetalCounterSamplePtr Sample = FMetalCounterSamplePtr(new FMetalCounterSample(EMetalCounterSampleType::BlitBoundary, Buffer, OutOffset));
BlitCommandEncoder->sampleCountersInBuffer(Buffer, OutOffset++, CVarMetalRHIInsertCounterSampleBarrier.GetValueOnAnyThread());
return Sample;
}