検証UE4: 4.27.2
まちがってたらごめんなさい。
まとめ
- 別のClusterに同一アセットが入っていると開放できない。
- 参照するアセットに注意。
- 開放する順番にも注意。
- Cluster Rootの対象を増やす際は注意。
- エンジンデフォルトだとParticleSystem/Material等だけで問題が出にくい
- 自前で増やす際は注意
参考
- [UE4]GCClusterとその動作を調査する方法 - Qiita EGJ-Takashi_Suzuki(Epic Games Japan)
- 『FINAL FANTASY VII REMAKE』におけるプロファイリングと最適化事例 【UNREAL FEST EXTREME 2021 SUMMER】 - docswell
Cluster Rootの対象
デフォルトでCluster Rootになり得るのは、下記のみかと思います。(C++側のCanBeClusterRootを確認)
・Material
・ParticleSystem(Cascadeの方)
プロジェクト設定で下記もRoot化できます。
・BP
→ 「ブループリントクラスタリングが有効」(gc.BlueprintClusteringEnabled)
またC++では CanBeClusterRoot を override してRoot化するか指定できます。
開放されないCluster
検証
こんな構成の、パーティクルシステムが配置されたサブレベルが有ります。
Level[A] - ParticleSystem[A] - Material[A] - Texture[Green]
Level[B] - ParticleSystem[B] - Material[B] - Texture[Green]
このレベルをLevelStreamingで下記の順でロード操作します。
1)LevelA -> ロード
2)LevelB -> ロード
3)LevelA -> アンロード
4)LevelB -> アンロード
- ロード前
-
Obj List: Class=ParticleSystem 0 Objects
-
- 1)LevelA -> ロード
-
Obj List: Class=ParticleSystem ParticleSystem /Game/PS_A.PS_A
-
- 2)LevelB -> ロード
-
Obj List: Class=ParticleSystem ParticleSystem /Game/PS_A.PS_A ParticleSystem /Game/PS_B.PS_B
-
- 3)LevelA -> アンロード
-
Obj List: Class=ParticleSystem ParticleSystem /Game/PS_A.PS_A ←※ ParticleSystem /Game/PS_B.PS_B
-
- 4)LevelB -> アンロード
-
Obj List: Class=ParticleSystem 0 Objects
-
(3)の「LevelA アンロード」の時点で"PS_A"は使用がなくなった筈ですが、開放されません。
(4)の「LevelB アンロード」されると開放されます。
問題
開放されないという事で、いつも通り誰が持ってるか調べようと参照元を辿る obj refs で見ると下記のように出てきます
obj refs Name=PS_A
LogReferenceChain: (standalone) (ClusterRoot) ParticleSystem /Game/PS_A.PS_A is not currently reachable.
調べようとしても誰が持ってるか分からない状態になっています。
この場合は、 gc.DumpRefsToCluster Root=<Root名> でCluster参照を出力できます。
gc.DumpRefsToCluster Root=PS_A
LogObj: Display: Dumping references to objects in cluster ParticleSystem /Game/PS_A.PS_A
LogReferenceChain: (root) (NeverGCed) GCObjectReferencer /Engine/Transient.GCObjectReferencer_2147482645::AddReferencedObjects(): FParticleSystemWorldManager
LogReferenceChain: ParticleSystemComponent /Game/LV_SubB.LV_SubB:PersistentLevel.PS_B_2.ParticleSystemComponent0->Template
LogReferenceChain: (standalone) (ClusterRoot) ParticleSystem /Game/PS_B.PS_B->Emitters
LogReferenceChain: (Clustered) ParticleSpriteEmitter /Game/PS_B.PS_B:ParticleSpriteEmitter_0->LODLevels
LogReferenceChain: (Clustered) ParticleLODLevel /Game/PS_B.PS_B:ParticleSpriteEmitter_0.ParticleLODLevel_0->RequiredModule
LogReferenceChain: (Clustered) ParticleModuleRequired /Game/PS_B.PS_B:ParticleModuleRequired_0->Material
LogReferenceChain: (standalone) (Clustered) Material /Game/M_ParticleB.M_ParticleB->TextureValues
LogReferenceChain: (standalone) (Clustered) Texture2D /Engine/EngineResources/AICON-Green.AICON-Green->Outer
LogReferenceChain: (Clustered) Package /Engine/EngineResources/AICON-Green
原因としては、 別々のClusterで同じアセットを参照している事が問題になります。
パーティクルシステムはClusterRootになるObjectです。
PS_A, PS_BはそれぞれClusterのRootになっています。
gc.ListClusters
LogObj: Display: ParticleSystem /Game/PS_A.PS_A (Index: 15945), Size: 15, ReferencedClusters: 0
LogObj: Display: ParticleSystem /Game/PS_B.PS_B (Index: 15970), Size: 13, ReferencedClusters: 1
LogObj: Display: Displayed 2 clusters
LogObj: Display: Total number of clusters: 2
PS_A, PS_Bの2つで使用しているテクスチャが同じアセットを使用しています。(上記赤枠)
ロード順で、該当テクスチャは先にロードされたPS_Aのクラスタに入っています。
その為、PS_Aを開放するにはテクスチャの開放が必要。
→ 参照先(PS_B)が開放されていないと、PS_Aは開放できません。
対策
- Cluster Rootの対象を考慮する
- エンジンのデフォルト的に今回はParticleSystemですが、
前述の設定であるようにBPやC++で独自に対象にすると問題が色々出る事が考えられます。
- エンジンのデフォルト的に今回はParticleSystemですが、
- 強制的に開放する
該当の開放できない状態((3)のLevelAアンロードした状態)で、を実行してClusterを開放。次にGCが走ると、開放できなかったPS_Aが開放されます。gc.CreateGCClusters 0
必要があれば再度 gc.CreateGCClusters 1 に戻すとか。
開放する順番について
アンロード順を変更して、Bを先に開放すると、問題なく開放されます。
PS_Bで使用する該当のテクスチャアセットが、PS_Aクラスタに入っている為
1)LevelA -> ロード
2)LevelB -> ロード
3)LevelB -> アンロード
4)LevelA -> アンロード
- ロード前
-
Obj List: Class=ParticleSystem 0 Objects
-
- 1)LevelA -> ロード
-
Obj List: Class=ParticleSystem ParticleSystem /Game/PS_A.PS_A
-
- 2)LevelB -> ロード
-
Obj List: Class=ParticleSystem ParticleSystem /Game/PS_A.PS_A ParticleSystem /Game/PS_B.PS_B
-
- 3)LevelB -> アンロード
-
Obj List: Class=ParticleSystem ParticleSystem /Game/PS_B.PS_A
-
- 4)LevelA -> アンロード
-
Obj List: Class=ParticleSystem 0 Objects
-