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(),使用起來更加方便。

 

 

 

 

 

 

 

 

 

 

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