ini 読み込みの最適化が発生しました。この機会に、UE エンジンの設定ファイル階層と読み取りプロセスを記録して学習してください。
2つの質問
プロジェクトのディレクトリ構造を見るときに、
同じレベルの保存ディレクトリの下に config があり、saved:saved/config に別の設定があるのはなぜですか? それらの違いは何ですか?
LoadExternalIniFile
ファントム読み取り設定ファイルのソース コードを確認すると、設定ファイルを読み取る関数が使用されていることがわかります。ただし、この関数は読み取り操作に加えて書き込み操作も実行します。Unreal に設定ファイルをロードした後にファイルを書き込む必要があるのはなぜですか?
階層構造
実際、UE が採用する config 構成ファイルの読み取り戦略はレイヤーの形式になっており、次の 4 つのレベルに分割できます。
- エンジン/構成
- エンジン/保存済み/設定
- [プロジェクト名]/構成
- [プロジェクト名]/保存済み/構成
「ConfigFileName」という名前の構成ファイルをロードするとき、UE はこれらのディレクトリを順番に走査します。後で読み取られるものは前のものをカバーします。つまり、それらの優先順位は順番に増加します。
この階層の概念は非常に興味深いです。これはサブクラスの複製に少し似ています。プロジェクト内のディレクトリに ini ConofigFileName がある場合は、プロジェクト内の ini を使用します。それ以外の場合は、親エンジン ディレクトリの ConofigFileName.ini が使用されます。
1 つのプロジェクトの ini を変更しても他のプロジェクトには影響しないことが保証されており、変更されていない部分では引き続き一般設定が使用されます。
UE が読み取る設定ファイル ディレクトリは、ConfigCacheIni.cpp の GConfigLayers に記録されます。
// ConfigCacheIni.cpp
GConfigLayers[] =
{
/**************************************************
**** CRITICAL NOTES
**** If you change this array, you need to also change EnumerateConfigFileLocations() in ConfigHierarchy.cs!!!
**** And maybe UObject::GetDefaultConfigFilename(), UObject::GetGlobalUserConfigFilename()
**************************************************/
// Engine/Base.ini
{
TEXT("AbsoluteBase"), TEXT("{ENGINE}/Config/Base.ini"), EConfigLayerFlags::Required | EConfigLayerFlags::NoExpand},
// Engine/Base*.ini
{
TEXT("Base"), TEXT("{ENGINE}/Config/Base{TYPE}.ini") },
// Engine/Platform/BasePlatform*.ini
{
TEXT("BasePlatform"), TEXT("{ENGINE}/Config/{PLATFORM}/Base{PLATFORM}{TYPE}.ini") },
// Project/Default*.ini
{
TEXT("ProjectDefault"), TEXT("{PROJECT}/Config/Default{TYPE}.ini"), EConfigLayerFlags::AllowCommandLineOverride | EConfigLayerFlags::GenerateCacheKey },
// Project/Generated*.ini Reserved for files generated by build process and should never be checked in
{
TEXT("ProjectGenerated"), TEXT("{PROJECT}/Config/Generated{TYPE}.ini"), EConfigLayerFlags::GenerateCacheKey },
// Engine/Platform/Platform*.ini
{
TEXT("EnginePlatform"), TEXT("{ENGINE}/Config/{PLATFORM}/{PLATFORM}{TYPE}.ini") },
// Project/Platform/Platform*.ini
{
TEXT("ProjectPlatform"), TEXT("{PROJECT}/Config/{PLATFORM}/{PLATFORM}{TYPE}.ini") },
// Project/Generated*.ini Reserved for files generated by build process and should never be checked in
{
TEXT("ProjectPlatformGenerated"), TEXT("{PROJECT}/Config/{PLATFORM}/Generated{PLATFORM}{TYPE}.ini") },
// UserSettings/.../User*.ini
{
TEXT("UserSettingsDir"), TEXT("{USERSETTINGS}Unreal Engine/Engine/Config/User{TYPE}.ini"), EConfigLayerFlags::NoExpand },
// UserDir/.../User*.ini
{
TEXT("UserDir"), TEXT("{USER}Unreal Engine/Engine/Config/User{TYPE}.ini"), EConfigLayerFlags::NoExpand },
// Project/User*.ini
{
TEXT("GameDirUser"), TEXT("{PROJECT}/Config/User{TYPE}.ini"), EConfigLayerFlags::GenerateCacheKey | EConfigLayerFlags::NoExpand },
};
ファイル読み込み処理
初期化プロセス
読み取られたini設定ファイルはグローバル変数GConfigにキャッシュされます。
ファイル読み込み処理
主に時間がかかるのは、構成の読み取りと構成の書き込みの 2 つの位置です。
ファイル書き込み
UE が前述の階層トラバーサルを実行した後、最後に有効となる設定ファイルを取得します。このファイルの内容を Saved ディレクトリに書き込みます。
Engine/Config からのみ読み取り、サブレベルには上書きされたファイルはなく、Engine/Saved/Config に書き込みます。それ以外の場合は、[ProjectName]/Saved/Config に書き込みます。ここでの書き方は違いだけを書いていることに注意してください。
プロパティの書き込みでは、このプロパティの値がそのクラスの CDO と同じである場合、複数のコピーを保存する必要はなく、構成ファイルは書き込まれません。
同じ情報の複数のコピーが走査に保存されないように、書き込み操作には複数のネストされたfor
走査が含まれます。ini ファイルが非常に大きい場合、ConfigFile.Write
この操作には時間がかかることがあります。
これで、最初の問題は解決されました。Saved が指定されたパス内のファイルは実行時に書き込まれ、Save が指定されていないファイルは読み取り用の元の構成になります。
- エンジン/コンフィグ【オリジナルコンフィグ】
- Engine/Saved/Config [エンジンの実行後に書き込む]
- [プロジェクト名]/Config【元の構成】
- [プロジェクト名]/Saved/Config [プロジェクトの実行後に書き込みます]
ここで質問があります: ini ファイルを初期化してロードするときに、なぜ保存された場所に書き込む必要があるのですか? UE は書き込み操作内で比較を行っていますが、自身の親レベルの内容と差異がない場合、保存されたディレクトリには書き込まれません。ただし、理論的には、GConfig にロードされたばかりのコンテンツは、初期ロード中に変更されません。
設定ファイルの内容と GConfig の構造
構成ファイルを開いてその構造を確認します:
ConfigFile のファイル構造は次のように抽象化されています:
GConfig も完全にこの構造に従って構築されています:
GConfig はキー値としてのファイル名TMap<FString,FConfigFile>
、FConfigFile はキーとしてのセクション名TMap<FString,FConfigSection>
、FConfigSection は各セクションの下のキーと値のペアです。