You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user