[UE C++]リソースの読み込み (2) リソースの検索 ——FindObject
上記の記事では最後に同期読み込みと非同期読み込みを導入する必要があるとありましたが、その前にFindObjectXXXX
リソースを検索するための API を導入する必要があると感じています。
よく使われるのはFindObject
、FindObjectFast
、FindObjectChecked
、でありFindObjectSafe
、FSoftObjectPath::ResolveObject
その呼び出し関係はおおよそ次のとおりです。
各ステージで実行される作業を分析する例を見てみましょうFindObject
(次のシーケンスはその呼び出しプロセスです)。使用例は記事の最後に示されています。
1.オブジェクトの検索
機能:メモリ内のオブジェクトを検索し、見つかった場合は返します。見つからなかった場合は nullptr が返され、ロードはトリガーされません。
template< class T >
inline T* FindObject( UObject* Outer, const TCHAR* Name, bool ExactClass=false )
{
return (T*)StaticFindObject( T::StaticClass(), Outer, Name, ExactClass );
}
FindObject
これはテンプレート化されておりStaticFindObject
、入力パラメータは次のように機能します。
- 外側: 外側が渡された場合、対応するリソース オブジェクトは外側が配置されているパッケージの下に見つかります。外側 (ANY_PACKAGE) がない場合、リソース オブジェクトはグローバルに見つかります。
- 名前: NamePrivate、オブジェクトの一意の識別子。Object->GetFName() を通じて取得できます。(この名前は参照パスにすることもできます。これは後で分析されます)
- ExactClass: 完全に一致するかどうか。 false はサブクラスと一致します。
2.静的検索オブジェクト
関数:コア関数は、受信した OrigInName(Name) を解析し、InObjectPackage(Outer) を設定することです。これが、FindObject
実行フロー内のStaticFindObject
一部の受信した参照パスをサポートする理由です。コア コードは次のとおりです。
UObject* StaticFindObject( UClass* ObjectClass, UObject* InObjectPackage, const TCHAR* OrigInName, bool ExactClass )
{
·······
// Resolve the object and package name.
const bool bAnyPackage = InObjectPackage==ANY_PACKAGE;
UObject* ObjectPackage = bAnyPackage ? nullptr : InObjectPackage;
········
FName ObjectName;
// Don't resolve the name if we're searching in any package
if (!bAnyPackage)
{
FString InName = OrigInName;
if (!ResolveName(ObjectPackage, InName, false, false))
{
return nullptr;
}
ObjectName = FName(*InName, FNAME_Add);
}
else
{
FString InName = OrigInName;
ConstructorHelpers::StripObjectClass(InName);
ObjectName = FName(*InName, FNAME_Add);
}
return StaticFindObjectFast(ObjectClass, ObjectPackage, ObjectName, ExactClass, bAnyPackage);
}
When bAnyPackage = false
, a function will be used ResolveName
to parse the OrigInName. OrigInName に有効な PackageName が含まれている場合は、新しい ObjectPackage と ObjectName が生成されます。
UObject* ObjectPackage = nullptr;
FString InName{"/Game/Mannequin/Character/Materials/M_Male_Body.M_Male_Body"};
ResolveName(ObjectPackage, InName, false, false);
上記のコードに示すようにResolveName
、ObjectPackage->GetFName() = “/Game/Mannquin/Character/materials/M_ Male_Body” の後、 InName = “M_ Male_Body” となります。ResolveName
内部的には、FindObject
ObjectPackage も検索されます。
3.静的FindObjectFast
機能:いくつかの判断 (GIsSavingPackage、GC、AsyncLoading) を行ってから、StaticFindObjectFastInternal
4.静的FindObjectFastInternal
機能: UObjectHashTable を取得し、スレッドセーフに渡します。StaticFindObjectFastInternalThreadSafe
FUObjectHashTables& ThreadHash = FUObjectHashTables::Get();
次の引用は、UObjectHashTables を使用した UObjectHash の管理からのものです。
UObject は UE オブジェクト システムの基本コンポーネントです。通常、ゲーム内には何十万もの UObject があります。それらは異なるタイプに属し、異なるアウターとパッケージを持ち、各 UObject には独自の名前もあります。UObjectHashTables は、ゲーム内の UObject を保存および管理するリレーショナル データベースのようなもので、外部の更新およびクエリ インターフェイスを提供します。クエリが主な目的で、UObjectHashTables を使用すると、特定の UClass が持つ UObject のインスタンスの数を簡単にクエリしたり、Name などに基づいて対応する UObject をクエリしたりすることができます。これらはすべて非常に実用的な関数です。FUObjectHashTables を使用して実装されます。
StaticFindObjectFastInternalThreadSafe
UObjectHashTables のHashとHashOuterが使用されます
/** Hash sets */
TMap<int32, FHashBucket> Hash;
TMultiMap<int32, class UObjectBase*> HashOuter;
オブジェクトの完全名には、オブジェクト自体の名前 (NamePrivate) とすべての外部の外部名が含まれます。
Hashはオブジェクトの NamePrivate マッピングのハッシュ テーブル、HashOuterはオブジェクトの NamePrivate + OutdoorNames マッピングのハッシュ テーブルです。
5. StaticFindObjectFastInternalThreadSafe
検索は 2 つの状況に分けられます。ObjectPackage == nullptr
5.1 オブジェクトパッケージ != nullptr
この場合、オブジェクトのアウターは既知なので、 UObjectHashTables のHashOuterで直接検索できます。まず、ハッシュ値を取得する必要があります。
int32 Hash = GetObjectOuterHash(ObjectName, (PTRINT)ObjectPackage);
その後、競合を防ぐために UObjectHashTables がロックされます。
FHashTableLock HashLock(ThreadHash);
次のステップでは、HashOuter のハッシュ値に対応するすべての UObjectbBase* を取得してフィルタリングします。フィルタリング条件は次のとおりです。
if
(
/* check that the name matches the name we're searching for */
(Object->GetFName() == ObjectName)
/* Don't return objects that have any of the exclusive flags set */
&& !Object->HasAnyFlags(ExcludeFlags)
/* check that the object has the correct Outer */
&& Object->GetOuter() == ObjectPackage
/** If a class was specified, check that the object is of the correct class */
&& (ObjectClass == nullptr || (bExactClass ? Object->GetClass() == ObjectClass : Object->IsA(ObjectClass)))
/** Include (or not) pending kill objects */
&& !Object->HasAnyInternalFlags(ExclusiveInternalFlags)
)
具体的には、UObjectのソースコード解析(3) - FindObjectから
- リソースパスは一致する必要があります
- 特定のフラグは含めることができません。たとえば、リソースの準備がまだ整っていないため、最初にそのサブオブジェクトをインスタンス化する必要があります。
- アウターは正しい必要があります
- ロードしたいクラスが適切であること、存在しないクラスをロードできないことを確認してください。
- GC が実行されようとしている、または一時停止されている特定のオブジェクトはロードできません。
5.2 ObjectPackage == nullptr
ObjectPackage == nullptr の場合、2 つの状況があり、1 つは ANY_PACKAGE としてマークされ、もう 1 つはオブジェクトのアウターが実際にnullptr の
補助クエリ構造オブジェクト (トップレベル UObject) を作成する場合です。
FObjectSearchPath SearchPath(ObjectName);
FObjectSearchPath は、ObjectName、分割パッケージ名、サブオブジェクトなどを解析するために使用されます。
Splits an object path into FNames representing an outer chain.
Input path examples: "Object", "Package.Object", "Object:Subobject", "Object:Subobject.Nested", "Package.Object:Subobject", "Package.Object:Subobject.NestedSubobject"
メンバー変数は 2 つあります
FName Inner;
TArray<FName, TInlineAllocator<8>> Outers;
ObjectName = "Package.Object" の場合、Inner = "Object"、Outers[0] = "Package"
次のステップでは、ハッシュ値を取得し、競合を防ぐために UObjectHashTables をロックします。ただし、Outer は不明で、Object の NamePrivate しか知らないため、 UObjectHashTables のハッシュハッシュ テーブルで検索されます。
const int32 Hash = GetObjectHash(SearchPath.Inner);
FHashTableLock HashLock(ThreadHash);
FHashBucket* Bucket = ThreadHash.Hash.Find(Hash);
次の引用は、UObjectHashTables を使用した UObjectHash の管理からのものです。
FHashBucket は興味深いデータ構造で、メモリ使用量を削減するために UE が独自の経験に基づいて特別に最適化しているように感じられます。ハッシュ キーはオブジェクトの FName であるため、複数のオブジェクトのハッシュ キーが同じである場合があります。通常、TMap < int32, TSet < Uobject*>> データ構造を使用する必要がありますが、ほとんどのオブジェクトが同じ名前を持たない場合は、要素が 1 つだけの TSet が多数存在します。TSet はハッシュ テーブルであるため、大量のメモリの無駄が発生します。ただし、これは単なる仮説であり、同じ名前のオブジェクトがいくつあるかは、特定のゲームによって異なります。
次に、FHashBucket を走査して条件付きフィルタリングを実行します。フィルタリング条件は 1 つだけ追加され、それが ANY_PACKAGE であるかどうかの判断になります。同時に、Object->GetFName() とアウターの一致には少し違いがあります。
if
(
/* check that the name matches the name we're searching for */
(Object->GetFName() == SearchPath.Inner)
/* Don't return objects that have any of the exclusive flags set */
&& !Object->HasAnyFlags(ExcludeFlags)
/*If there is no package (no InObjectPackage specified, and InName's package is "")
and the caller specified any_package, then accept it, regardless of its package.
Or, if the object is a top-level package then accept it immediately.*/
&& (bAnyPackage || !Object->GetOuter())
/** If a class was specified, check that the object is of the correct class */
&& (ObjectClass == nullptr || (bExactClass ? Object->GetClass() == ObjectClass : Object->IsA(ObjectClass)))
/** Include (or not) pending kill objects */
&& !Object->HasAnyInternalFlags(ExclusiveInternalFlags)
/** Ensure that the partial path provided matches the object found */
&& SearchPath.MatchOuterNames(Object->GetOuter())
)
6.FindObjectFast
上記では FindObject の実行処理を紹介しましたが、以下の API を含め FindObjectFast の処理は FindObject と大きく重複しますが、上記の処理を明確にした上で、実際の開発ではさまざまな状況を考慮して API を呼び出して使用します。
機能:オブジェクトを迅速に検索します。後続の呼び出しは直接行われるためStaticFindObjectFast
、入力 Name の呼び出しStaticFindObject
やFindObjectFast
解析は行われません。入力 Inner = Object->OuterPrivate および Name = Object->NamePrivate が正常に検索できることを確認する必要があります。 。より高速で、高パフォーマンスのシナリオに適しています。
template< class T >
inline T* FindObjectFast( UObject* Outer, FName Name, bool ExactClass=false, bool AnyPackage=false, EObjectFlags ExclusiveFlags=RF_NoFlags )
{
return (T*)StaticFindObjectFast( T::StaticClass(), Outer, Name, ExactClass, AnyPackage, ExclusiveFlags );
}
アサートをトリガーするように、Outer を ANY_PACKAGE に設定することはできないことに注意してください。
7.FindObjectChecked
機能:内部呼び出しStaticFindObjectChecked
、オブジェクトが見つからない場合、nullptr は返されず、Fatal が直接報告され、プログラムが停止されます (出荷版とテスト版は停止しません)。
template< class T >
inline T* FindObjectChecked( UObject* Outer, const TCHAR* Name, bool ExactClass=false )
{
return (T*)StaticFindObjectChecked( T::StaticClass(), Outer, Name, ExactClass );
}
UObject* StaticFindObjectChecked( UClass* ObjectClass, UObject* ObjectParent, const TCHAR* InName, bool ExactClass )
{
UObject* Result = StaticFindObject( ObjectClass, ObjectParent, InName, ExactClass );
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if( !Result )
{
UE_LOG(LogUObjectGlobals, Fatal, TEXT("Failed to find object '%s %s.%s'"), *ObjectClass->GetName(), ObjectParent==ANY_PACKAGE ? TEXT("Any") : ObjectParent ? *ObjectParent->GetName() : TEXT("None"), InName);
}
#endif
return Result;
}
8.FindObjectSafe
機能:内部呼び出しStaticFindObjectSafe
。GIsSavingPackage = true または IsGarbageCollectingOnGameThread() = true の場合、nullptr が直接返され、Fatal は報告されません。
template< class T >
inline T* FindObjectSafe( UObject* Outer, const TCHAR* Name, bool ExactClass=false )
{
return (T*)StaticFindObjectSafe( T::StaticClass(), Outer, Name, ExactClass );
}
UObject* StaticFindObjectSafe( UClass* ObjectClass, UObject* ObjectParent, const TCHAR* InName, bool ExactClass )
{
if (!GIsSavingPackage && !IsGarbageCollectingOnGameThread())
{
FGCScopeGuard GCAndSavepackageGuard;
return StaticFindObject( ObjectClass, ObjectParent, InName, ExactClass );
}
else
{
return NULL;
}
}
9. FSoftObjectPath::ResolveObject
機能:この関数の機能は前の記事で説明しました。FSoftObjectPath によって参照されるリソースがメモリにロードされているかどうかを判断します。ロードされている場合はリソース オブジェクト ポインタが返され、そうでない場合は null が返されます。
内部的には、いくつかの条件付き判断を実行するために最初に呼び出されResolveObjectInternal
、次に呼び出されます。FindObject
UObject* FoundObject = FindObject<UObject>(nullptr, PathString);
例
"/Game/Mannequin/Character/Materials/M_Male_Body.M_Male_Body"
リソースがメモリ内に存在すると仮定します
成功:
- FindObject(nullptr, TEXT(“/ゲーム/マネキン/キャラクター/マテリアル/M_男性_ボディー.M_男性_ボディー”))
- FindObject(ANY_PACKAGE, TEXT(“/ゲーム/マネキン/キャラクター/マテリアル/M_男性_ボディー.M_男性_ボディー”))
- FindObject(nullptr, TEXT(“マテリアル'/ゲーム/マネキン/キャラクター/マテリアル/M_男性_ボディ.M_男性_ボディ'”))
- FindObject(ANY_PACKAGE, TEXT(“M_ Male_Body”))
- FindObject(ANY_PACKAGE, TEXT(“M_ Male_Body.M_ Male_Body”))
- FindObjectFast(FindObject(nullptr, TEXT(“/ゲーム/マネキン/キャラクター/マテリアル/M_男性_ボディー”)), TEXT(“M_男性_ボディー”))
失敗:
- FindObjectFast( ANY_PACKAGE , TEXT(“M_ Male_Body.M_ Male_Body”)) //崩溃
- FindObjectFast(FindObject(nullptr, TEXT(“/ゲーム/マネキン/キャラクター/マテリアル/M_男性_ボディー”)), TEXT(“ M_男性_ボディー.M_男性_ボディー”))
- FindObject(FindObject(nullptr, TEXT(“/ゲーム/マネキン/キャラクター/マテリアル/M_男性_ボディー”)), TEXT(“ M_男性_ボディー.M_男性_ボディー”))