103 lines
2.8 KiB
C++
103 lines
2.8 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MetalTempAllocator.h"
|
|
#include "MetalDevice.h"
|
|
#include "MetalRHIPrivate.h"
|
|
#include "MetalDynamicRHI.h"
|
|
#include "MetalProfiler.h"
|
|
|
|
FMetalTempAllocator::FMetalTempAllocator(FMetalDevice& InDevice, uint32_t InMinAllocationSize, uint32_t InTargetAllocationLimit, uint32_t InAlignment)
|
|
: Device(InDevice),
|
|
MinAllocationSize(InMinAllocationSize),
|
|
TargetAllocationLimit(InTargetAllocationLimit),
|
|
Alignment(InAlignment)
|
|
{
|
|
TotalAllocationStat = GET_STATID(STAT_MetalTempAllocatorAllocatedMemory);
|
|
}
|
|
|
|
FMetalBufferPtr FMetalTempAllocator::Allocate(const uint32_t Size)
|
|
{
|
|
FScopeLock lock(&AllocatorLock);
|
|
|
|
uint32 AlignedSize = Align(Size, Alignment);
|
|
|
|
FTempBufferInfo* TempBuffer = nullptr;
|
|
for(FTempBufferInfo& Buffer : Buffers)
|
|
{
|
|
if(Buffer.Size - Buffer.Offset >= AlignedSize)
|
|
{
|
|
TempBuffer = &Buffer;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(TempBuffer == nullptr)
|
|
{
|
|
uint32_t BufferSize = FMath::Max((uint32_t)MinAllocationSize, AlignedSize);
|
|
TempBuffer = &Buffers.AddDefaulted_GetRef();
|
|
|
|
TotalAllocated += BufferSize;
|
|
INC_MEMORY_STAT_BY_FName(TotalAllocationStat.GetName(), BufferSize);
|
|
|
|
TempBuffer->Offset = 0;
|
|
TempBuffer->Size = BufferSize;
|
|
TempBuffer->Buffer = Device.GetDevice()->newBuffer(BufferSize,
|
|
MTL::ResourceCPUCacheModeWriteCombined |
|
|
MTL::ResourceStorageModeShared);
|
|
|
|
if(!TempBuffer->Buffer)
|
|
{
|
|
UE_LOG(LogMetal, Fatal, TEXT("Failed to allocate MTL::Buffer in MetalTempAllocator::Allocate"));
|
|
}
|
|
}
|
|
|
|
FMetalBufferPtr Buffer = FMetalBufferPtr(new FMetalBuffer(TempBuffer->Buffer, NS::Range(TempBuffer->Offset, AlignedSize), this));
|
|
TempBuffer->Offset += AlignedSize;
|
|
|
|
if(!Buffer)
|
|
{
|
|
UE_LOG(LogMetal, Fatal, TEXT("Failed to allocate FMetalBuffer in MetalTempAllocator::Allocate"));
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
void FMetalTempAllocator::Cleanup()
|
|
{
|
|
FScopeLock lock(&AllocatorLock);
|
|
|
|
TArray<FTempBufferInfo> OldBuffers;
|
|
|
|
// Move all buffers that have been used in this window
|
|
for(FTempBufferInfo TempBuffer : Buffers)
|
|
{
|
|
if(TempBuffer.Offset != 0)
|
|
{
|
|
TempBuffer.Offset = 0;
|
|
OldBuffers.Add(TempBuffer);
|
|
}
|
|
}
|
|
|
|
Buffers.RemoveAll([](const FTempBufferInfo& TempBuffer) { return TempBuffer.Offset != 0; });
|
|
|
|
// Ensure that buffers are re-added to the pool when fences are complete (if we are below the target limit)
|
|
FMetalDynamicRHI::Get().DeferredDelete([this, OldBuffers]()
|
|
{
|
|
FScopeLock lock(&AllocatorLock);
|
|
for(const FTempBufferInfo& Buffer : OldBuffers)
|
|
{
|
|
if((TotalAllocated + Buffer.Size) <= TargetAllocationLimit)
|
|
{
|
|
Buffers.Add(Buffer);
|
|
}
|
|
else
|
|
{
|
|
Buffer.Buffer->setPurgeableState(MTL::PurgeableStateEmpty);
|
|
Buffer.Buffer->release();
|
|
TotalAllocated -= Buffer.Size;
|
|
DEC_MEMORY_STAT_BY_FName(TotalAllocationStat.GetName(), Buffer.Size);
|
|
}
|
|
}
|
|
});
|
|
}
|