UE4 AssetRegistry分析

UE4 AssetRegistry分析

https://zhuanlan.zhihu.com/p/76964514

简介

Asset Registry是editor的子系统,负责在editor加载时收集未加载的asset信息。这些信息储存在内存中,因此editor可以创建资源列表,而不需要真正加载这些资源。Content Browser是这个系统的主要消费者,但是editor的其他部分,以及editor外的模块也能访问它们。

详细介绍可见官方文档:https://docs.unrealengine.com/en-US/Programming/Assets/Registry/index.html

和AssetRegistry相关的主要实体,以及它们见的关系如下图所示:

简而言之,AssetRegisty可以搜寻uasset文件,并用FAssetData抽象表示,并在有需要时根据FAssetData中的路径线索加载UObject。

 

主要数据结构

  • FAssetData

用于在AssetRegistry查找asset时,抽象的表示一个asset,其中包含了一些关于asset的重要信息,可以在不加载UObject,即不进行反序列化操作和UObject内存分配这些比较耗的操作情况下表示关于这个对象的关键信息。因此ContentBrowser中虽然可以看到所有asset,但它们包含的uobject并没有加载到内存中。

主要属性:

FName ObjectPath:这个asset的object path,形如PackageName.AssetName,一个package中只有顶层object才会有AssetData.

FName PackageName:这个asset所在Package的名称,形如/Game/Path/Package

FName AssetClass:asset类型的类名称。比如蓝图资源则为“Blueprint”

FAssetDataTagMapSharedView TagsAndValues:一个uproperty的名称和值组成的map,可以提供一些资源所对应对象的描述信息,这些property被标记为AssetRegistrySearchable,或者在所在类的GetAssetRegistryTags()函数中被添加。比如蓝图资源asset就提供了GeneratedClass,BlueprintType等信息。

TArray<int32> ChunkIDs:所在chunk的ID

 

  • FAssetPackageData

是对磁盘上package的抽象描述,在package进行save/load操作时同步更新。

主要属性:

int64 DiskSize:asset在磁盘上所占空间

FGuid PackageGuid:package的guid,用于唯一标识一个package

 

  • FAssetIdentifier

用于唯一标识一个在asset registry框架下可以被引用的“实体”,可以表示一个package,也可以表示一个object

主要属性:

FName PackageName:依赖的package名称

FPrimaryAssetType PrimaryAssetType:primary asset类型,如果被设置,ObjectName即为PrimaryAssetName

FName ObjectName:package中具体的object名称,如果为空,那么就表示默认asset

FName ValueName:被引用的具体值,比如ObjectName描述了类似UStruct的类型

 

  • FARFilter

用作AssetRegistry进行查询时的过滤器,可以配置多个属性。

部分可配置属性:

TArray<FName> PackageNames:Package名称的白名单

TArray<FName> PackagePaths:Package路径的白名单

bool bRecursivePaths:Package路径是否为递归查询

TMultiMap<FName, TOptional<FString>> TagsAndValues:被标为AssetRegistrySearchable的属性的值白名单

 

FARFilter的属性大多为一个容器,可配置多个元素。每个容器属性内部多个元素间的逻辑关系为“OR”,而多个属性间的逻辑关系为“AND”。

 

  • FAssetRegistryState

储存了读取asset后的相关信息,用作磁盘内容的cache数据,用于描述asset registry当前的状态,可以由IAssetRegistry进行使用和维护。把这些数据单独组成一个FAssetRegistryState类,是为了减少耦合性,因为引擎中除asset registry外的其他模块 也需要用到这些数据。

主要属性:

TMap<FName, FAssetData*> CachedAssetsByObjectPath:ObjectPath名称到对应asset的映射

TMap<FName, TArray<FAssetData*> > CachedAssetsByPackageName:package名称到其包含的asset映射

TMap<FName, TArray<FAssetData*> > CachedAssetsByClass:class name到磁盘上asset的映射

主要方法:

GetAssets():根据FARFilter获取已加载的符合条件的FAssetData

 

  • IAssetRegistry

AssetRegistry使用了基于UInterface的模式,因此IAssetRegistry接口定义了AssetRegistry需要具备的操作。

主要方法:

Tick:通过Tick来实现异步asset搜索,每当有asset新被搜索到,tick中就会把资源加入

GetAssetsByClass:得到某个class类型的所有已加载asset

ScanPathsAndFilesSynchronous:搜索某些路径下的asset

SearchAllAssets:搜索所有的asset,可通过命令行调用,editor启动时也会调用

 

  • UAssetRegistryImpl

是IAssetRegistry的实现,单例模式。

 

  • FAssetDataGatherer

用于在文件中搜寻asset。是异步task,内部使用了FAssetDataDiscovery进行更底层的搜寻操作。同时也支持同步搜寻。

 

  • FDependsNode

可以抽象的描述一个资源节点(package/object)的依赖与引用关系,储存于FAssetRegistryState中,也扮演着cache的角色。当我们要查询一个资源的引用视图,比如编辑器中的“reference viwer”功能,就可以使用该数据。

主要属性:

FAssetIdentifier Identifier:该节点对应asset实体的id

TArray<FDependsNode*> HardDependencies:该资源的直接引用资源列表(加载该资源时这些被引用的资源也会加载)

TArray<FDependsNode*> SoftDependencies:该资源的间接引用资源列表

TArray<FDependsNode*> Referencers:引用该资源的资源列表

 

  • FPackageDependencyData

FAssetDataDiscovery扫描磁盘上的资源时得到的数据结构,继承自FLinkerTables,之后要把它转为FDependsNode。

 

  • Asset Registry类图

 

如何搜寻asset文件

使用AssetRegistry,我们可以方便的从磁盘上读取asset文件并构建FAssetData描述。

  • 同步方式

通过上层接口进行调用,通常只能通过同步的方式搜寻,通常使用以下接口:

ScanFilesSynchronous(const TArray<FString>& InFilePaths, bool bForceRescan = false)

搜寻InFilePaths路径下的所有asset,不需要返回值,因为搜寻后的结果会保存在FAssetRegistryState中,之后可以使用过滤器进行查询。具体方式为新建一个FAssetDataGatherer,把模式设置为同步,然后FAssetDataGatherer就会直接阻塞的调用run()方法,为我们搜寻asset了。

类似的,也可以使用ScanFilesSynchronous方法进行同步搜索。

 SearchAllAssets(bool bSynchronousSearch)

一种更简单的方式:同步搜寻所有assets。SearchAllAssets方法可以以同步和异步方式运行,该方法的同步模式通常用于CommandLet中,因为此时对时间消耗并不是很敏感。此方法同步模式最终执行方式其实是和ScanFilesSynchronous相同的,只是为我们自动得到了所有要搜寻的路径。

 

  • 异步方式

异步方式通常由编辑器内部调用,尚不清楚如何通过上层进行调用。

 SearchAllAssets(bool bSynchronousSearch)

之前说了,SearchAllAssets可以以异步模式执行,其实现方法也比较直观,为新建了一个FAssetDataGatherer对象,然后在UAssetRegistryImpl::Tick中获取。直接调用异步SearchAllAssets的地方目前只有一处,就是UAssetRegistryImpl的构造函数中,当在编辑器中,且不通过CommandLet时,就会调用,其作用是初始化ContentBrowser。我们刚打开编辑器时的初始化加载过程就是在做异步资源发现操作。

让我们看一下Tick是如何执行的,调用过程为:

  • EngineTick()
  • UEditorEngine::Tick 编辑器EngineTick
  • FAssetRegistryModule::TickAssetRegistry
  • UAssetRegistryImpl::Tick

在UAssetRegistryImpl::Tick中,会不断通过FAssetDataGatherer类型的成员变量BackgroundAssetSearch获取新搜寻到的FAssetData,并重置BackgroundAssetSearch的搜寻结果。之后AssetRegistry就会更新自己和State的数据,并发送一些广播,通知搜寻到 了新的assetdata。通过调用过程可以看到,在引擎的循环中,如果是编辑器,那FAssetRegistry的Tick始终在执行,以此达到保持编辑器中assetdata始终于磁盘同步的目的。

 AddPathToSearch(const FString& Path)

这个方法是私有方法,并不能被上层调用,作用为向异步扫描过程中加入待扫描路径,编辑器自身的一些功能模块会调用到这。

 

如何从FAssetData获取Uobject

既然FAssetData中已经包含了ObjectPath,那么当我们需要某个UObject实例时,简单的Load一下即可。不过FAssetData已经为我们包装好了一个函数:GetAsset(),使用起来更加方便。

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章