控制 AssetRegistry 的序列化

AssetRegistry 其实主要是在 Editor 下用来方便进行资源的查找和过滤操作,它的主要使用者是 ContentBrowser,这一点在 UE 的文档中也有描述:Asset Registry

对于项目而言在 Runtime 可能没有需求来使用它,但是在 AssetRegistry 模块一启动就会把 AssetRegistry.bin 加载到内存中,如果对它没有需求其实这部分内存是浪费的。

关闭 AssetRegistry 的影响:无法在运行时通过 AssetRegistry 模块进行资源的依赖分析、以及通过 AssetRegistry 检测资源是否存在的判断。

好在 UE 提供了不序列化或者部分序列化 AssetRegistry 数据的方法,在 UAssetRegistryImpl 的构造函数中会调用 InitializeSerializationOptionsFromIni 函数来读取 DefaultEngine.ini 中的配置,并会构造出一个 FAssetRegistrySerializationOptions 结构来存储,它会在后续的 Serialize 函数中使用,用来控制把哪部分的数据序列化到 AssetRegistry 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Runtime/AssetRegistry/Private/AssetRegistry.cpp
void UAssetRegistryImpl::InitializeSerializationOptionsFromIni(FAssetRegistrySerializationOptions& Options, const FString& PlatformIniName) const
{
FConfigFile* EngineIni = nullptr;
#if WITH_EDITOR
// Use passed in platform, or current platform if empty
FConfigFile PlatformEngineIni;
FConfigCacheIni::LoadLocalIniFile(PlatformEngineIni, TEXT("Engine"), true, (!PlatformIniName.IsEmpty() ? *PlatformIniName : ANSI_TO_TCHAR(FPlatformProperties::IniPlatformName())));
EngineIni = &PlatformEngineIni;
#else
// In cooked builds, always use the normal engine INI
EngineIni = GConfig->FindConfigFile(GEngineIni);
#endif

EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeAssetRegistry"), Options.bSerializeAssetRegistry);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeDependencies"), Options.bSerializeDependencies);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeNameDependencies"), Options.bSerializeSearchableNameDependencies);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeManageDependencies"), Options.bSerializeManageDependencies);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializePackageData"), Options.bSerializePackageData);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bUseAssetRegistryTagsWhitelistInsteadOfBlacklist"), Options.bUseAssetRegistryTagsWhitelistInsteadOfBlacklist);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bFilterAssetDataWithNoTags"), Options.bFilterAssetDataWithNoTags);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bFilterDependenciesWithNoTags"), Options.bFilterDependenciesWithNoTags);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bFilterSearchableNames"), Options.bFilterSearchableNames);
// ...
}

这个控制方式可以在打包时控制是否生成 AssetRegistry.bin,以及控制在运行时反序列化哪些 AssetRegistry 的数据(但是不会对 DevelopmentAssetRegistry.bin 造成影响,可以用它来进行资产审计)。

它的反序列化流程为:

  1. 检测 bSerializeAssetRegistry,如果为true 则把 AssetRegistry.bin 以二进制形式加载到内存中
  2. 通过 Serialize 函数来把二进制数据反序列化
  3. 释放加载 AssetRegistry.bin 所占用的内存

所以,AssetRegistry 的内存占用是在序列化之后的数据,而 FAssetRegistrySerializationOptions 就是控制把哪些数据序列化的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Runtime/AssetRegistry/Public/AssetRegistryState.h
/** Load/Save options used to modify how the cache is serialized. These are read out of the AssetRegistry section of Engine.ini and can be changed per platform. */
struct FAssetRegistrySerializationOptions
{
/** True rather to load/save registry at all */
bool bSerializeAssetRegistry;

/** True rather to load/save dependency info. If true this will handle hard and soft package references */
bool bSerializeDependencies;

/** True rather to load/save dependency info for Name references, */
bool bSerializeSearchableNameDependencies;

/** True rather to load/save dependency info for Manage references, */
bool bSerializeManageDependencies;

/** If true will read/write FAssetPackageData */
bool bSerializePackageData;

/** True if CookFilterlistTagsByClass is a whitelist. False if it is a blacklist. */
bool bUseAssetRegistryTagsWhitelistInsteadOfBlacklist;

/** True if we want to only write out asset data if it has valid tags. This saves memory by not saving data for things like textures */
bool bFilterAssetDataWithNoTags;

/** True if we also want to filter out dependency data for assets that have no tags. Only filters if bFilterAssetDataWithNoTags is also true */
bool bFilterDependenciesWithNoTags;

/** Filter out searchable names from dependency data */
bool bFilterSearchableNames;

/** The map of classname to tag set of tags that are allowed in cooked builds. This is either a whitelist or blacklist depending on bUseAssetRegistryTagsWhitelistInsteadOfBlacklist */
TMap<FName, TSet<FName>> CookFilterlistTagsByClass;

FAssetRegistrySerializationOptions()
: bSerializeAssetRegistry(false)
, bSerializeDependencies(false)
, bSerializeSearchableNameDependencies(false)
, bSerializeManageDependencies(false)
, bSerializePackageData(false)
, bUseAssetRegistryTagsWhitelistInsteadOfBlacklist(false)
, bFilterAssetDataWithNoTags(false)
, bFilterDependenciesWithNoTags(false)
, bFilterSearchableNames(false)
{}

/** Options used to read/write the DevelopmentAssetRegistry, which includes all data */
void ModifyForDevelopment()
{
bSerializeAssetRegistry = bSerializeDependencies = bSerializeSearchableNameDependencies = bSerializeManageDependencies = bSerializePackageData = true;
DisableFilters();
}

/** Disable all filters */
void DisableFilters()
{
bFilterAssetDataWithNoTags = false;
bFilterDependenciesWithNoTags = false;
bFilterSearchableNames = false;
}
};

配置的读取在以下代码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Runtime/AssetRegistry/Private/AssetRegistry.cpp
void UAssetRegistryImpl::InitializeSerializationOptionsFromIni(FAssetRegistrySerializationOptions& Options, const FString& PlatformIniName) const
{
FConfigFile* EngineIni = nullptr;
#if WITH_EDITOR
// Use passed in platform, or current platform if empty
FConfigFile PlatformEngineIni;
FConfigCacheIni::LoadLocalIniFile(PlatformEngineIni, TEXT("Engine"), true, (!PlatformIniName.IsEmpty() ? *PlatformIniName : ANSI_TO_TCHAR(FPlatformProperties::IniPlatformName())));
EngineIni = &PlatformEngineIni;
#else
// In cooked builds, always use the normal engine INI
EngineIni = GConfig->FindConfigFile(GEngineIni);
#endif

EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeAssetRegistry"), Options.bSerializeAssetRegistry);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeDependencies"), Options.bSerializeDependencies);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeNameDependencies"), Options.bSerializeSearchableNameDependencies);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializeManageDependencies"), Options.bSerializeManageDependencies);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bSerializePackageData"), Options.bSerializePackageData);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bUseAssetRegistryTagsWhitelistInsteadOfBlacklist"), Options.bUseAssetRegistryTagsWhitelistInsteadOfBlacklist);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bFilterAssetDataWithNoTags"), Options.bFilterAssetDataWithNoTags);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bFilterDependenciesWithNoTags"), Options.bFilterDependenciesWithNoTags);
EngineIni->GetBool(TEXT("AssetRegistry"), TEXT("bFilterSearchableNames"), Options.bFilterSearchableNames);

TArray<FString> FilterlistItems;
if (Options.bUseAssetRegistryTagsWhitelistInsteadOfBlacklist)
{
EngineIni->GetArray(TEXT("AssetRegistry"), TEXT("CookedTagsWhitelist"), FilterlistItems);
}
else
{
EngineIni->GetArray(TEXT("AssetRegistry"), TEXT("CookedTagsBlacklist"), FilterlistItems);
}

{
// this only needs to be done once, and only on builds using USE_COMPACT_ASSET_REGISTRY
TArray<FString> AsFName;
EngineIni->GetArray(TEXT("AssetRegistry"), TEXT("CookedTagsAsFName"), AsFName);
TArray<FString> AsPathName;
EngineIni->GetArray(TEXT("AssetRegistry"), TEXT("CookedTagsAsPathName"), AsPathName);
TArray<FString> AsLocText;
EngineIni->GetArray(TEXT("AssetRegistry"), TEXT("CookedTagsAsLocText"), AsLocText);
FAssetRegistryState::IngestIniSettingsForCompact(AsFName, AsPathName, AsLocText);
}
// ...

}

Config/DefaultEngine.ini 中创建AssetRegistrySection 使用上面的名字就可以控制 AssetRegistry 的序列化,减少打包时的包体大小以及内存占用(AssetRegistry 在引擎启动时会加载到内存中)

1
2
3
4
5
6
[AssetRegistry]
bSerializeAssetRegistry=false
bSerializeDependencies=false
bSerializeNameDependencies=false
bSerializeManageDependencies=false
bSerializePackageData=false

也可以对某个平台来单独指定,只需要修改平台相关的 Ini 文件:

1
2
3
Config/Windows/WindowsEngine.ini
Config/Android/AndroidEngine.ini
Config/IOS/IOSEngine.ini