Spine の UE4 ランタイムが盛大にメモリリークを引き起こしていたので、Deleaker というツールを使って調べてみた。結果からいうと、Spine プラグインのコードに以下のような変更を加えることでリークしなくなった。
diff --git a/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp b/Plugins/SpinePlugin/Sou
rce/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp
index 6e1b5ada..f9612ee6 100644
--- a/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp
+++ b/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp
@@ -41,6 +41,7 @@ USpineSkeletonRendererComponent::USpineSkeletonRendererComponent (const FObjectI
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
bAutoActivate = true;
+ bUseAsyncCooking = true;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> NormalMaterialRef(TEXT("/SpinePlugin/SpineUnlitNorm
alMaterial"));
NormalBlendMaterial = NormalMaterialRef.Object;
ここで加えた bUseAsyncCooking
という変数は、プロシージャルメッシュの生成の動作に影響を与える。プロシージャルメッシュが生成されるとき、内部でコリジョンのアップデートのために下のような関数が呼ばれる:
void UProceduralMeshComponent::UpdateCollision()
{
(...)
if(bUseAsyncCook)
{
UseBodySetup->CreatePhysicsMeshesAsync(FOnAsyncPhysicsCookFinished::CreateUObject(this, &UProceduralMeshComponent::FinishPhysicsAsyncCook, UseBodySetup));
}
else
{
// New GUID as collision has changed
UseBodySetup->BodySetupGuid = FGuid::NewGuid();
// Also we want cooked data for this
UseBodySetup->bHasCookedCollisionData = true;
UseBodySetup->InvalidatePhysicsData();
UseBodySetup->CreatePhysicsMeshes();
RecreatePhysicsState();
}
}
ここで bUseAsyncCook
が偽の時は、FGuid::NewGuid()
が呼ばれるんだけど、これが内部的に Windows API の CoCreateGuid という関数を呼び出し、これがクリティカルセクションを作る。
なぜクリティカルセクションがひどいメモリリークにつながるのかは分からないけど、この部分が実行されないように bUseAsyncCook
を真に設定したら実際にメモリリークが起こらなくなった。
上の画像が変更後で、下が変更前。これを見ると、変更前は何らかの原因で GC が阻害されていたように見える。
Twitter のスレッド:Toru Hisai on Twitter: "https://t.co/ycdj5D9s13 Spine の UE4 ランタイムが盛大にメモリリークしているようなので、Deleaker というのを試してみる。" / Twitter
#UE4 #Spine