Delay delete all buffer objects. Fixes a rare gpu corruption issue when buffer memory is reused

#rb michael.sartain
#jira UE-94299

#ROBOMERGE-OWNER: jeanmichel.dignard
#ROBOMERGE-AUTHOR: jonas.meyer
#ROBOMERGE-SOURCE: CL 13676867 in //UE4/Release-4.25/... via CL 13676887 via CL 13676903 via CL 13676926
#ROBOMERGE-BOT: TOOLS (Main -> Dev-Tools-Staging) (v707-13641620)

[CL 13685400 by jonas meyer in Dev-Tools-Staging branch]
This commit is contained in:
jonas meyer
2020-06-16 11:56:31 -04:00
10 changed files with 263 additions and 19 deletions

View File

@@ -807,7 +807,7 @@ void FVulkanCommandBufferPool::FreeUnusedCmdBuffers(FVulkanQueue* InQueue)
InQueue->GetLastSubmittedInfo(LastSubmittedCmdBuffer, LastSubmittedFenceCounter);
// Deferred deletion queue caches pointers to cmdbuffers
FDeferredDeletionQueue& DeferredDeletionQueue = Device->GetDeferredDeletionQueue();
FDeferredDeletionQueue2& DeferredDeletionQueue = Device->GetDeferredDeletionQueue();
for (int32 Index = CmdBuffers.Num() - 1; Index >= 0; --Index)
{

View File

@@ -64,6 +64,77 @@ struct FOptionalVulkanDeviceExtensions
return HasAMDBufferMarker || HasNVDiagnosticCheckpoints;
}
};
namespace VulkanRHI
{
class FDeferredDeletionQueue2 : public FDeviceChild
{
public:
FDeferredDeletionQueue2(FVulkanDevice* InDevice);
~FDeferredDeletionQueue2();
enum class EType
{
RenderPass,
Buffer,
BufferView,
Image,
ImageView,
Pipeline,
PipelineLayout,
Framebuffer,
DescriptorSetLayout,
Sampler,
Semaphore,
ShaderModule,
Event,
ResourceAllocation,
BufferSuballocation,
};
template <typename T>
inline void EnqueueResource(EType Type, T Handle)
{
static_assert(sizeof(T) <= sizeof(uint64), "Vulkan resource handle type size too large.");
EnqueueGenericResource(Type, (uint64)Handle);
}
void EnqueueResourceAllocation(TRefCountPtr<VulkanRHI::FOldResourceAllocation> ResourceAllocation);
void EnqueueBufferSuballocation(TRefCountPtr<VulkanRHI::FBufferSuballocation> Allocation);
void EnqueueBufferSuballocationDirect(FBufferSuballocation* SubAllocation);
void ReleaseResources(bool bDeleteImmediately = false);
inline void Clear()
{
ReleaseResources(true);
}
void OnCmdBufferDeleted(FVulkanCmdBuffer* CmdBuffer);
private:
void EnqueueGenericResource(EType Type, uint64 Handle);
struct FEntry
{
EType StructureType;
uint32 FrameNumber;
uint64 FenceCounter;
FVulkanCmdBuffer* CmdBuffer;
uint64 Handle;
TRefCountPtr<VulkanRHI::FOldResourceAllocation> ResourceAllocation;
TRefCountPtr<VulkanRHI::FBufferSuballocation> SubAllocation;
VulkanRHI::FBufferSuballocation* SubAllocationDirect;
};
FCriticalSection CS;
TArray<FEntry> Entries;
};
}
class FVulkanDevice
{
@@ -215,7 +286,7 @@ public:
return ResourceHeapManager;
}
inline VulkanRHI::FDeferredDeletionQueue& GetDeferredDeletionQueue()
inline VulkanRHI::FDeferredDeletionQueue2& GetDeferredDeletionQueue()
{
return DeferredDeletionQueue;
}
@@ -358,7 +429,7 @@ private:
VulkanRHI::FResourceHeapManager ResourceHeapManager;
VulkanRHI::FDeferredDeletionQueue DeferredDeletionQueue;
VulkanRHI::FDeferredDeletionQueue2 DeferredDeletionQueue;
VulkanRHI::FStagingManager StagingManager;

View File

@@ -152,6 +152,7 @@ FVulkanResourceMultiBuffer::~FVulkanResourceMultiBuffer()
for (uint32 Index = 0; Index < NumBuffers; ++Index)
{
Size += Buffers[Index]->GetSize();
Device->GetDeferredDeletionQueue().EnqueueBufferSuballocation(Buffers[Index]);
}
UpdateVulkanBufferStats(Size, BufferUsageFlags, false);
}

View File

@@ -2299,10 +2299,11 @@ namespace VulkanRHI
FGPUEvent::~FGPUEvent()
{
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Event, Handle);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Event, Handle);
}
/// Note: FDeferredDeletionQueue is deprecated internally, and replaced by FDeferredDeletionQueue2. it is left only for patch compatibility, and should -not- be used
FDeferredDeletionQueue::FDeferredDeletionQueue(FVulkanDevice* InDevice)
: FDeviceChild(InDevice)
{
@@ -2419,6 +2420,176 @@ namespace VulkanRHI
}
}
FDeferredDeletionQueue2::FDeferredDeletionQueue2(FVulkanDevice* InDevice)
: FDeviceChild(InDevice)
{
}
FDeferredDeletionQueue2::~FDeferredDeletionQueue2()
{
check(Entries.Num() == 0);
}
void FDeferredDeletionQueue2::EnqueueGenericResource(EType Type, uint64 Handle)
{
FVulkanQueue* Queue = Device->GetGraphicsQueue();
FEntry Entry;
Entry.SubAllocationDirect = 0;
Entry.StructureType = Type;
Queue->GetLastSubmittedInfo(Entry.CmdBuffer, Entry.FenceCounter);
Entry.FrameNumber = GVulkanRHIDeletionFrameNumber;
Entry.Handle = Handle;
{
FScopeLock ScopeLock(&CS);
#if VULKAN_HAS_DEBUGGING_ENABLED
FEntry* ExistingEntry = Entries.FindByPredicate([&](const FEntry& InEntry)
{
return InEntry.Handle == Entry.Handle;
});
checkf(ExistingEntry == nullptr, TEXT("Attempt to double-delete resource, FDeferredDeletionQueue::EType: %d, Handle: %llu"), (int32)Type, Handle);
#endif
Entries.Add(Entry);
}
}
void FDeferredDeletionQueue2::EnqueueResourceAllocation(TRefCountPtr<VulkanRHI::FOldResourceAllocation> ResourceAllocation)
{
FVulkanQueue* Queue = Device->GetGraphicsQueue();
FEntry Entry;
Entry.SubAllocationDirect = 0;
Entry.StructureType = EType::ResourceAllocation;
Queue->GetLastSubmittedInfo(Entry.CmdBuffer, Entry.FenceCounter);
Entry.FrameNumber = GVulkanRHIDeletionFrameNumber;
Entry.Handle = VK_NULL_HANDLE;
Entry.ResourceAllocation = ResourceAllocation;
{
FScopeLock ScopeLock(&CS);
Entries.Add(Entry);
}
}
void FDeferredDeletionQueue2::EnqueueBufferSuballocation(TRefCountPtr<VulkanRHI::FBufferSuballocation> SubAllocation)
{
FVulkanQueue* Queue = Device->GetGraphicsQueue();
FEntry Entry;
Entry.SubAllocationDirect = 0;
Entry.StructureType = EType::BufferSuballocation;
Queue->GetLastSubmittedInfo(Entry.CmdBuffer, Entry.FenceCounter);
Entry.FrameNumber = GVulkanRHIDeletionFrameNumber;
Entry.Handle = VK_NULL_HANDLE;
Entry.SubAllocation = SubAllocation;
{
FScopeLock ScopeLock(&CS);
Entries.Add(Entry);
}
}
void FDeferredDeletionQueue2::EnqueueBufferSuballocationDirect(FBufferSuballocation* SubAllocation)
{
FVulkanQueue* Queue = Device->GetGraphicsQueue();
FEntry Entry;
Entry.SubAllocationDirect = 0;
Entry.StructureType = EType::BufferSuballocation;
Queue->GetLastSubmittedInfo(Entry.CmdBuffer, Entry.FenceCounter);
Entry.FrameNumber = GVulkanRHIDeletionFrameNumber;
Entry.Handle = VK_NULL_HANDLE;
Entry.SubAllocationDirect = SubAllocation;
{
FScopeLock ScopeLock(&CS);
Entries.Add(Entry);
}
}
void FDeferredDeletionQueue2::ReleaseResources(bool bDeleteImmediately)
{
#if VULKAN_ENABLE_AGGRESSIVE_STATS
SCOPE_CYCLE_COUNTER(STAT_VulkanDeletionQueue);
#endif
FScopeLock ScopeLock(&CS);
VkDevice DeviceHandle = Device->GetInstanceHandle();
// Traverse list backwards so the swap switches to elements already tested
for (int32 Index = Entries.Num() - 1; Index >= 0; --Index)
{
FEntry* Entry = &Entries[Index];
// #todo-rco: Had to add this check, we were getting null CmdBuffers on the first frame, or before first frame maybe
if (bDeleteImmediately ||
(GVulkanRHIDeletionFrameNumber > Entry->FrameNumber + NUM_FRAMES_TO_WAIT_FOR_RESOURCE_DELETE &&
(Entry->CmdBuffer == nullptr || Entry->FenceCounter < Entry->CmdBuffer->GetFenceSignaledCounterC()))
)
{
switch (Entry->StructureType)
{
#define VKSWITCH(Type, ...) case EType::Type: __VA_ARGS__; VulkanRHI::vkDestroy##Type(DeviceHandle, (Vk##Type)Entry->Handle, VULKAN_CPU_ALLOCATOR); break
VKSWITCH(RenderPass);
VKSWITCH(Buffer);
VKSWITCH(BufferView);
VKSWITCH(Image);
VKSWITCH(ImageView);
VKSWITCH(Pipeline, DEC_DWORD_STAT(STAT_VulkanNumPSOs));
VKSWITCH(PipelineLayout);
VKSWITCH(Framebuffer);
VKSWITCH(DescriptorSetLayout);
VKSWITCH(Sampler);
VKSWITCH(Semaphore);
VKSWITCH(ShaderModule);
VKSWITCH(Event);
#undef VKSWITCH
case EType::BufferSuballocation:
case EType::ResourceAllocation:
Entry->ResourceAllocation.SafeRelease();
Entry->SubAllocation.SafeRelease();
if (Entry->SubAllocationDirect)
{
delete Entry->SubAllocationDirect;
Entry->SubAllocationDirect = 0;
}
break;
default:
check(0);
break;
}
Entries.RemoveAtSwap(Index, 1, false);
}
}
}
void FDeferredDeletionQueue2::OnCmdBufferDeleted(FVulkanCmdBuffer* DeletedCmdBuffer)
{
FScopeLock ScopeLock(&CS);
for (int32 Index = 0; Index < Entries.Num(); ++Index)
{
FEntry& Entry = Entries[Index];
if (Entry.CmdBuffer == DeletedCmdBuffer)
{
Entry.CmdBuffer = nullptr;
}
}
}
FTempFrameAllocationBuffer::FTempFrameAllocationBuffer(FVulkanDevice* InDevice)
: FDeviceChild(InDevice)
, BufferIndex(0)
@@ -2580,7 +2751,7 @@ namespace VulkanRHI
check(SemaphoreHandle != VK_NULL_HANDLE);
if (!bExternallyOwned)
{
Device.GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Semaphore, SemaphoreHandle);
Device.GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Semaphore, SemaphoreHandle);
}
SemaphoreHandle = VK_NULL_HANDLE;
}

View File

@@ -188,7 +188,7 @@ FVulkanPipeline::~FVulkanPipeline()
#endif
if (Pipeline != VK_NULL_HANDLE)
{
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Pipeline, Pipeline);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Pipeline, Pipeline);
Pipeline = VK_NULL_HANDLE;
}
/* we do NOT own Layout !*/
@@ -2217,7 +2217,7 @@ void FVulkanRHIGraphicsPipelineState::DeleteVkPipeline(bool bImmediate)
}
else
{
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Pipeline, VulkanPipeline);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Pipeline, VulkanPipeline);
}
VulkanPipeline = VK_NULL_HANDLE;
}

View File

@@ -598,7 +598,7 @@ public:
}
else
{
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Pipeline, Pipeline);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Pipeline, Pipeline);
}
Pipeline = VK_NULL_HANDLE;
}

View File

@@ -1150,7 +1150,7 @@ FVulkanBuffer::~FVulkanBuffer()
// The buffer should be unmapped
check(BufferPtr == nullptr);
Device.GetDeferredDeletionQueue().EnqueueResource(FDeferredDeletionQueue::EType::Buffer, Buf);
Device.GetDeferredDeletionQueue().EnqueueResource(FDeferredDeletionQueue2::EType::Buffer, Buf);
Buf = VK_NULL_HANDLE;
Device.GetMemoryManager().Free(Allocation);
@@ -1486,7 +1486,7 @@ void FVulkanBufferView::Destroy()
if (View != VK_NULL_HANDLE)
{
DEC_DWORD_STAT(STAT_VulkanNumBufferViews);
Device->GetDeferredDeletionQueue().EnqueueResource(FDeferredDeletionQueue::EType::BufferView, View);
Device->GetDeferredDeletionQueue().EnqueueResource(FDeferredDeletionQueue2::EType::BufferView, View);
View = VK_NULL_HANDLE;
ViewId = 0;
}
@@ -1616,7 +1616,7 @@ FVulkanRenderPass::~FVulkanRenderPass()
{
DEC_DWORD_STAT(STAT_VulkanNumRenderPasses);
Device.GetDeferredDeletionQueue().EnqueueResource(FDeferredDeletionQueue::EType::RenderPass, RenderPass);
Device.GetDeferredDeletionQueue().EnqueueResource(FDeferredDeletionQueue2::EType::RenderPass, RenderPass);
RenderPass = VK_NULL_HANDLE;
}
@@ -1695,7 +1695,8 @@ FVulkanRingBuffer::FVulkanRingBuffer(FVulkanDevice* InDevice, uint64 TotalSize,
FVulkanRingBuffer::~FVulkanRingBuffer()
{
delete BufferSuballocation;
Device->GetDeferredDeletionQueue().EnqueueBufferSuballocationDirect(BufferSuballocation);
BufferSuballocation = 0;
}
uint64 FVulkanRingBuffer::WrapAroundAllocateMemory(uint64 Size, uint32 Alignment, FVulkanCmdBuffer* InCmdBuffer)

View File

@@ -148,7 +148,7 @@ void FVulkanShader::PurgeShaderModules()
for (const auto& Pair : ShaderModules)
{
VkShaderModule ShaderModule = Pair.Value;
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::ShaderModule, ShaderModule);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::ShaderModule, ShaderModule);
}
ShaderModules.Empty(0);
}
@@ -263,7 +263,7 @@ FVulkanLayout::~FVulkanLayout()
{
if (PipelineLayout != VK_NULL_HANDLE)
{
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::PipelineLayout, PipelineLayout);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::PipelineLayout, PipelineLayout);
PipelineLayout = VK_NULL_HANDLE;
}
}

View File

@@ -749,7 +749,7 @@ void FVulkanSurface::Destroy()
if (Image != VK_NULL_HANDLE)
{
Size = GetMemorySize();
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Image, Image);
Device->GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Image, Image);
Device->GetDeferredDeletionQueue().EnqueueResourceAllocation(GetResourceAllocation());
Image = VK_NULL_HANDLE;
}
@@ -1745,7 +1745,7 @@ void FVulkanTextureView::Destroy(FVulkanDevice& Device)
if (View)
{
DEC_DWORD_STAT(STAT_VulkanNumImageViews);
Device.GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::ImageView, View);
Device.GetDeferredDeletionQueue().EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::ImageView, View);
Image = VK_NULL_HANDLE;
View = VK_NULL_HANDLE;
ViewId = 0;

View File

@@ -470,16 +470,16 @@ FVulkanFramebuffer::~FVulkanFramebuffer()
void FVulkanFramebuffer::Destroy(FVulkanDevice& Device)
{
VulkanRHI::FDeferredDeletionQueue& Queue = Device.GetDeferredDeletionQueue();
VulkanRHI::FDeferredDeletionQueue2& Queue = Device.GetDeferredDeletionQueue();
// will be deleted in reverse order
Queue.EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::Framebuffer, Framebuffer);
Queue.EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Framebuffer, Framebuffer);
Framebuffer = VK_NULL_HANDLE;
for (int32 Index = 0; Index < AttachmentViewsToDelete.Num(); ++Index)
{
DEC_DWORD_STAT(STAT_VulkanNumImageViews);
Queue.EnqueueResource(VulkanRHI::FDeferredDeletionQueue::EType::ImageView, AttachmentViewsToDelete[Index]);
Queue.EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::ImageView, AttachmentViewsToDelete[Index]);
}
for (int32 Index = 0; Index < ResourceAllocationsToDelete.Num(); ++Index)