How to Properly capture image using render targets and free them and performance monitoring for rendertargets.

Hello Everyone,

I’m working on a high frequency image generator (does some processing after that so can’t really rely on current plugins and solutions), and I’m using readsurfacefloatdata for 12 images each of 512X512 pixels, the image generation is launched as a separate gamethread which uses enqueue_render_command to add the image reading. Since this is in high frequency to avoid accidental read/write from the same rendertarget, Im creating a new one for each call, and freeing them up after usage by doing

rendertarget->ReleaseResource();
rendertarget->ConditionalBeginDestroy();
rendertarget= nullptr;

but often this leads to crashes with vulkanRHI, where a certain memory is requested but vulkan fails allocate it and high number of this warning is printed in the terminal. how to monitor the render target usage, and to find the cause of these crashes? any help or resource is appreciated, thank you for your time and effort.

System config:
OS:Linux (Ubuntu 22.04)
Processor: AMD Ryzen 5 3600
GPU: RTX 2080 8GB
RAM: 32 GB
UnrealEngine : 5.4
NVIDIA Driver: 560.35.03
this is from a recent crash,

Fatal error: [File:./Runtime/VulkanRHI/Private/VulkanMemory.cpp] [Line: 1995] 
Out of memory on Vulkan; MemoryTypeIndex=1, AllocSize=256.000MB
0x000078020a1a7807 libUnrealEditor-VulkanRHI.so!VulkanRHI::FVulkanResourceHeap::AllocateResource(VulkanRHI::FVulkanAllocation&, FVulkanEvictable*, VulkanRHI::EType, unsigned int, unsigned int, bool, bool, VulkanRHI::EVulkanAllocationMetaType, bool, char const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanMemory.cpp:1995]
0x000078020a1acad4 libUnrealEditor-VulkanRHI.so!VulkanRHI::FMemoryManager::AllocateBufferMemory(VulkanRHI::FVulkanAllocation&, FVulkanEvictable*, VkMemoryRequirements const&, unsigned int, VulkanRHI::EVulkanAllocationMetaType, bool, bool, char const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanMemory.cpp:3008]
0x000078020a1ad0a8 libUnrealEditor-VulkanRHI.so!VulkanRHI::FMemoryManager::AllocateBufferMemory(VulkanRHI::FVulkanAllocation&, VkBuffer_T*, VulkanRHI::EVulkanAllocationFlags, char16_t const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanMemory.cpp:3056]
0x000078020a26892b libUnrealEditor-VulkanRHI.so!FVulkanTransientHeap::FVulkanTransientHeap(FRHITransientHeap::FInitializer const&, FVulkanDevice*) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanTransientResourceAllocator.cpp:36]
0x000078020a268d2a libUnrealEditor-VulkanRHI.so!FVulkanTransientHeapCache::CreateHeap(FRHITransientHeap::FInitializer const&) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanTransientResourceAllocator.cpp:86]
0x0000780270636e0c libUnrealEditor-RHICore.so!FRHITransientHeapCache::Acquire(unsigned long long, ERHITransientHeapFlags) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RHICore/Private/RHICoreTransientResourceAllocator.cpp:619]
0x0000780270637b60 libUnrealEditor-RHICore.so!FRHITransientResourceHeapAllocator::CreateBufferInternal(FRHIBufferCreateInfo const&, char16_t const*, unsigned int, unsigned int, unsigned int, TFunction<FRHITransientBuffer* (FRHITransientHeap::FResourceInitializer const&)>) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RHICore/Private/RHICoreTransientResourceAllocator.cpp:739]
0x000078020a268ffc libUnrealEditor-VulkanRHI.so!FVulkanTransientResourceAllocator::CreateBuffer(FRHIBufferCreateInfo const&, char16_t const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanTransientResourceAllocator.cpp:118]
0x000078028da9d59c libUnrealEditor-RenderCore.so!FRDGBuilder::AllocateTransientResources(TArrayView<FRDGBuilder::FCollectResourceOp const, int>) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RenderCore/Private/RenderGraphBuilder.cpp:2792]
0x000078028da95900 libUnrealEditor-RenderCore.so!FRDGBuilder::Execute() [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RenderCore/Private/RenderGraphBuilder.cpp:1826]
0x0000780279bd5b83 libUnrealEditor-Renderer.so!UpdateSceneCaptureContent_RenderThread(FRHICommandListImmediate&, FSceneRenderer*, FRenderTarget*, FTexture*, FString const&, FRHICopyTextureInfo const&, bool, FGenerateMipsParams const&, bool, bool) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Renderer/Private/SceneCaptureRendering.cpp:524]
0x0000780279bd5002 libUnrealEditor-Renderer.so!FScene::UpdateSceneCaptureContents(USceneCaptureComponent2D*)::$_0::operator()(FRHICommandListImmediate&) const [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Renderer/Private/SceneCaptureRendering.cpp:1130]
0x000078028db03673 libUnrealEditor-RenderCore.so!UE::Core::Private::Function::TFunctionRefCaller<FRenderThreadCommandPipe::EnqueueAndLaunch(char16_t const*, unsigned int&, TStatId, TUniqueFunction<void (FRHICommandListImmediate&)>&&)::$_0, void ()>::Call(void*) [/mnt/horde/++UE5/Sync/Engine/Source/Runtime/Core/Public/Templates/Function.h:405]
0x000078028db568b3 libUnrealEditor-RenderCore.so!TGraphTask<TFunctionGraphTaskImpl<void (), (ESubsequentsMode::Type)1> >::ExecuteTask(TArray<FBaseGraphTask*, TSizedDefaultAllocator<32> >&, ENamedThreads::Type, bool) [/mnt/horde/++UE5/Sync/Engine/Source/Runtime/Core/Public/Async/TaskGraphInterfaces.h:1235]
0x0000780294fadcc3 libUnrealEditor-Core.so!FNamedTaskThread::ProcessTask




libUnrealEditor-VulkanRHI.so!VulkanRHI::FVulkanResourceHeap::AllocateResource(VulkanRHI::FVulkanAllocation&, FVulkanEvictable*, VulkanRHI::EType, unsigned int, unsigned int, bool, bool, VulkanRHI::EVulkanAllocationMetaType, bool, char const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanMemory.cpp:1995]
libUnrealEditor-VulkanRHI.so!VulkanRHI::FMemoryManager::AllocateBufferMemory(VulkanRHI::FVulkanAllocation&, FVulkanEvictable*, VkMemoryRequirements const&, unsigned int, VulkanRHI::EVulkanAllocationMetaType, bool, bool, char const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanMemory.cpp:3008]
libUnrealEditor-VulkanRHI.so!VulkanRHI::FMemoryManager::AllocateBufferMemory(VulkanRHI::FVulkanAllocation&, VkBuffer_T*, VulkanRHI::EVulkanAllocationFlags, char16_t const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanMemory.cpp:3056]
libUnrealEditor-VulkanRHI.so!FVulkanTransientHeap::FVulkanTransientHeap(FRHITransientHeap::FInitializer const&, FVulkanDevice*) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanTransientResourceAllocator.cpp:36]
libUnrealEditor-VulkanRHI.so!FVulkanTransientHeapCache::CreateHeap(FRHITransientHeap::FInitializer const&) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanTransientResourceAllocator.cpp:86]
libUnrealEditor-RHICore.so!FRHITransientHeapCache::Acquire(unsigned long long, ERHITransientHeapFlags) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RHICore/Private/RHICoreTransientResourceAllocator.cpp:619]
libUnrealEditor-RHICore.so!FRHITransientResourceHeapAllocator::CreateBufferInternal(FRHIBufferCreateInfo const&, char16_t const*, unsigned int, unsigned int, unsigned int, TFunction<FRHITransientBuffer* (FRHITransientHeap::FResourceInitializer const&)>) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RHICore/Private/RHICoreTransientResourceAllocator.cpp:739]
libUnrealEditor-VulkanRHI.so!FVulkanTransientResourceAllocator::CreateBuffer(FRHIBufferCreateInfo const&, char16_t const*, unsigned int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/VulkanRHI/Private/VulkanTransientResourceAllocator.cpp:118]
libUnrealEditor-RenderCore.so!FRDGBuilder::AllocateTransientResources(TArrayView<FRDGBuilder::FCollectResourceOp const, int>) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RenderCore/Private/RenderGraphBuilder.cpp:2792]
libUnrealEditor-RenderCore.so!FRDGBuilder::Execute() [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RenderCore/Private/RenderGraphBuilder.cpp:1826]
libUnrealEditor-Renderer.so!UpdateSceneCaptureContent_RenderThread(FRHICommandListImmediate&, FSceneRenderer*, FRenderTarget*, FTexture*, FString const&, FRHICopyTextureInfo const&, bool, FGenerateMipsParams const&, bool, bool) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Renderer/Private/SceneCaptureRendering.cpp:524]
libUnrealEditor-Renderer.so!FScene::UpdateSceneCaptureContents(USceneCaptureComponent2D*)::$_0::operator()(FRHICommandListImmediate&) const [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Renderer/Private/SceneCaptureRendering.cpp:1130]
libUnrealEditor-RenderCore.so!UE::Core::Private::Function::TFunctionRefCaller<FRenderThreadCommandPipe::EnqueueAndLaunch(char16_t const*, unsigned int&, TStatId, TUniqueFunction<void (FRHICommandListImmediate&)>&&)::$_0, void ()>::Call(void*) [/mnt/horde/++UE5/Sync/Engine/Source/Runtime/Core/Public/Templates/Function.h:405]
libUnrealEditor-RenderCore.so!TGraphTask<TFunctionGraphTaskImpl<void (), (ESubsequentsMode::Type)1> >::ExecuteTask(TArray<FBaseGraphTask*, TSizedDefaultAllocator<32> >&, ENamedThreads::Type, bool) [/mnt/horde/++UE5/Sync/Engine/Source/Runtime/Core/Public/Async/TaskGraphInterfaces.h:1235]
libUnrealEditor-Core.so!FNamedTaskThread::ProcessTasksNamedThread(int, bool) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Core/Private/Async/TaskGraph.cpp:760]
libUnrealEditor-Core.so!FNamedTaskThread::ProcessTasksUntilQuit(int) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Core/Private/Async/TaskGraph.cpp:650]
libUnrealEditor-RenderCore.so!RenderingThreadMain(FEvent*) [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RenderCore/Private/RenderingThread.cpp:413]
libUnrealEditor-RenderCore.so!FRenderingThread::Run() [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/RenderCore/Private/RenderingThread.cpp:564]
libUnrealEditor-Core.so!FRunnableThreadPThread::Run() [/mnt/horde/++UE5/Sync/Engine/Source/./Runtime/Core/Private/HAL/PThreadRunnableThread.cpp:25]
libUnrealEditor-Core.so!FRunnableThreadPThread::_ThreadProc(void*) [/mnt/horde/++UE5/Sync/Engine/Source/Runtime/Core/Private/HAL/PThreadRunnableThread.h:187]
libc.so.6!UnknownFunction(0x94ac2)
libc.so.6!UnknownFunction(0x12684f)

Hello there @dhashuml!

Checking through your log, there’s a call to an “Out of memory on Vulkan” error, usually tied to the rendering process being overloaded. Since your plan is to create and destroy renders very quickly, this could be exerting the memory beyond its capacity.

The best approach here would be to alter the logic of your plan, maybe establishing a limited pool size for your renders, process each other, then remove the group with garbare collector, to avoid the constant strain in memory.

As for monitoring, the following console commands should be useful:

  • r.Vulkan.Memory.Dump: shows Vulkan memory allocation information to the log (you shoud look for large allocations and fragmentation here)

  • stat memory: shows overall GPU and system memory usage

  • stat renderthread: shows insights into render thread and RHI performance

And of course, the tools included with Unreal Insight. Details regarding them can be found here:

Hello @brs-sebascova ,

thanks for you input, my next approach was that, to avoid creating new targets which occupy gpu memory, rather than reuse once a target is safe to use, but I also have noticed some other issues in the forum (Release a SceneCaptureComponent2D from GPU memory) where the user also saw that the render target resource wasn’t being released even after calling ReleaseResource and ConditionalBeginDestroy, which is kind of a similar problem I’m facing, since my targets aren’t that big, but overtime it accumulates, which is when I think this is happening.

thanks for info on the monitoring, i think I have already tried a few, will update here with relevant details from the monitoring.

I’m marking this as a solution, as I most probably will stick with reusing render targets. but I will be running the monitoring before that with current method and if I dont see the rendertargets being released after respective calls, will attach the relevant details for debugging.

thanks for your time and effort.

1 Like