Hi,
We’re looking at memory leaks in our project and I found a case that stood out to me in UE 5.6. I’m looking for clarification on how this should be working or if there are any known issues.
When we run the game on 5.6 and let it sit idle, memory insights reports leaking data from FDynamicSkelMeshObjectDataGPUSkin::InitDynamicSkelMeshObjectDataGPUSkin
I see that being called here:
// create the new dynamic data for use by the rendering thread
// this data is only deleted when another update is sent
FDynamicSkelMeshObjectDataGPUSkin* NewDynamicData = FDynamicSkelMeshObjectDataGPUSkin::AllocDynamicSkelMeshObjectDataGPUSkin();
NewDynamicData->InitDynamicSkelMeshObjectDataGPUSkin(
InDynamicData,
InSceneProxy,
InSkinnedAsset,
SkeletalMeshRenderData,
this,
LODIndex,
InActiveMorphTargets,
InMorphTargetWeights,
PreviousBoneTransformUpdateMode,
InExternalMorphWeightData);
if (!UpdateHandle.IsValid() || !UpdateHandle.Update(NewDynamicData))
{
FGPUSkinCache* GPUSkinCache = InSceneProxy ? InSceneProxy->GetScene().GetGPUSkinCache() : nullptr;
ENQUEUE_RENDER_COMMAND(SkelMeshObjectUpdateDataCommand)(UE::RenderCommandPipe::SkeletalMesh,
[this, GPUSkinCache, NewDynamicData](FRHICommandList& RHICmdList)
{
FScopeCycleCounter Context(GetStatId());
UpdateDynamicData_RenderThread(RHICmdList, GPUSkinCache, NewDynamicData);
});
}
That above call to UpdateDynamicData_RenderThread also looks to be what clears it - is this correct?
void FSkeletalMeshObjectGPUSkin::UpdateDynamicData_RenderThread(FRHICommandList& RHICmdList, FGPUSkinCache* GPUSkinCache, FDynamicSkelMeshObjectDataGPUSkin* InDynamicData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(GPUSkin::UpdateDynamicData_RT);
SCOPE_CYCLE_COUNTER(STAT_GPUSkinUpdateRTTime);
check(InDynamicData != nullptr);
CA_ASSUME(InDynamicData != nullptr);
bMorphNeedsUpdate = FDynamicSkelMeshObjectDataGPUSkin::IsMorphUpdateNeeded(DynamicData, InDynamicData);
if (DynamicData)
{
FDynamicSkelMeshObjectDataGPUSkin::FreeDynamicSkelMeshObjectDataGPUSkin(DynamicData);
}
But that code only fires if the below code returns false:
bool FSkeletalMeshUpdateChannel::Update(const FSkeletalMeshUpdateHandle& Handle, FSkeletalMeshDynamicData* MeshDynamicData)
{
check(IsInGameThread() || IsInParallelGameThread());
check(MeshDynamicData);
check(Handle.Channel == this);
if (GUseSkeletalMeshUpdater)
{
FOp Op;
Op.HandleIndex = Handle.Index;
Op.Type = FOp::EType::Update;
Op.Data_Update.MeshDynamicData = MeshDynamicData;
check(OpQueue);
OpQueue->Queue.Enqueue(Op);
OpQueue->NumUpdates.fetch_add(1, std::memory_order_relaxed);
OpQueue->Num.fetch_add(1, std::memory_order_relaxed);
return true;
}
return false;
}
What I’m unclear about is how this code could ever be getting called if a project is running with GUseSkeletalMeshUpdater enabled (which we are). Is there something else that should be freeing this data?