[UE C++] 资源加载(三) 同步加载——LoadObject
常用的同步加载方法有,LoadObject
,LoadClass
,LoadPackage
,FSoftObjectPath::TryLoad
,FStreamableManager::RequestSyncLoad
,FStreamableManager::LoadSynchronous
等。其中LoadObject
是最具代表性的加载过程,以下顺序是以LoadObject
和LoadClass
的执行流程来介绍。其它API执行流程读者可看下图进行参考,或者进入源码查看。
FStreamableManager::RequestSyncLoad
,FStreamableManager::LoadSynchronous
等同步加载方法,放到下一篇文章中和异步加载一起分析。使用示例在文章末尾。
1. LoadObject
作用: 首先会在内存中查找目标资源是否存在,若存在就会直接返回,若不存在会把路径转化为Package再进行LoadPackage
LoadObject
就是对StaticLoadObject
进行模板化
template< class T >
inline T* LoadObject( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
return (T*)StaticLoadObject( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}
输入参数:
- Outer: 资源的Outer,一个可选对象,用于缩小查找/加载对象的位置
- Name: 对象的字符串名称。如果它不是完整的引用路径,需要指定Outer和/或Filename
- Filename: 文件名称,可以从其中加载(或在文件的Package对象中找到)
- LoadFlags: 控制如何处理从磁盘加载的Flags,ELoadFlags enum
- Sandbox: 用于限制搜索对象的Package的列表,参数用于网络通信
2. StaticLoadObject
内部调用StaticLoadObjectInternal
,若没找到则会进行报错信息输出
3. StaticLoadObjectInternal
首先会调用ResolveName
,这个函数上篇文章提到过,作用就是对传入的Path进行解析,这个Path必须是完整路径或者是相对于传入的Outer的路径,随后会将Outer设置为Path所指的Package对象,StrName设置为Path所指对象的NamePrivate。
这里与StaticFindObject
调用ResolveName
不同的是,传入的后两个bool值为true,代表会尝试Load或Create不存在于内存中的Package以及当找不到Package时会抛出警告
// break up the name into packages, returning the innermost name and its outer
ResolveName(InOuter, StrName, true, true, LoadFlags & (LOAD_EditorOnly | LOAD_NoVerify | LOAD_Quiet | LOAD_NoWarn | LOAD_DeferDependencyLoads), InstancingContext);
而后调用StaticFindObjectFast
在内存中寻找资源对象,并判断对象是否加载完成,若完成则return
Result = StaticFindObjectFast(ObjectClass, InOuter, *StrName);
if (Result && Result->HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_WillBeLoaded))
{
// Object needs loading so load it before returning
Result = nullptr;
}
若没有在内存中找到目标资源对象,则考虑将资源对象所属的Package加载进内存中
一个包里如果有多个资源,他们在硬盘上对应的是同一个文件,那么只需要加载这个文件就好了
// now that we have one asset per package, we load the entire package whenever a single object is requested
LoadPackage(NULL, *InOuter->GetOutermost()->GetName(), LoadFlags & ~LOAD_Verify, nullptr, InstancingContext);
随后,会再次调用StaticFindObjectFast
在内存中寻找资源对象,若还没找到则会判断资源对象是否被重定向了,并通过FindObjectFast
查找
// If the object was not found, check for a redirector and follow it if the class matches
if (!Result && !(LoadFlags & LOAD_NoRedirects))
{
UObjectRedirector* Redirector = FindObjectFast<UObjectRedirector>(InOuter, *StrName);
if (Redirector && Redirector->DestinationObject && Redirector->DestinationObject->IsA(ObjectClass))
{
return Redirector->DestinationObject;
}
}
4. LoadPackage
作用: 加载与上下文标志匹配的Package和所有包含的对象。
内部调用LoadPackageInternal
,输入参数如下(只列举了前三个)
- InOuter: 如果指定了,则会以InOuter->GetPathName()为加载路径,若为nullptr则会以InLongPackageName为加载路径
- InLongPackageName: InOuter为nullptr则会以InLongPackageName为加载路径
- LoadFlags: 控制如何处理从磁盘加载的Flags,ELoadFlags enum
5. LoadPackageInternal
内部实现比较复杂,恕笔者能力不够,没有做分析
最终调用的是
LoadPackageAsync
函数,这就是异步加载的入口,并且最后FlushAsyncLoading
,内部阻塞等待,将异步加载转为同步。
LoadPackageAsync
函数,这个函数就是UE4资源加载的大入口,后面整套资源加载都隐藏在了这个函数之后
int32 RequestID = LoadPackageAsync(InName, nullptr, *InPackageName);
if (RequestID != INDEX_NONE)
{
FlushAsyncLoading(RequestID);
}
Result = (InOuter ? InOuter : FindObjectFast<UPackage>(nullptr, PackageFName));
return Result;
6. LoadClass
作用: 加载类资源
模板化的StaticLoadClass
,参数输入和LoadObject
完全一致。
template< class T >
inline UClass* LoadClass( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
return StaticLoadClass( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}
7. StaticLoadClass
内部调用了LoadObject
,随后对根据Path加载得到的UClass类型进行判断,若类型不匹配会报错并返回NULL
UClass* Class = LoadObject<UClass>( InOuter, InName, Filename, LoadFlags, Sandbox );
if( Class && !Class->IsChildOf(BaseClass) )
{
//报错信息
······
// return NULL class due to error
Class = NULL;
}
return Class;
使用示列
LoadObject —— no Outer
UTexture2D* TestTexture2D = LoadObject<UTexture2D>(nullptr, TEXT("/Game/StarterContent/Textures/T_Burst_M.T_Burst_M"));
LoadPackage
UPackage* TestPackage = LoadPackage(nullptr, TEXT("/Game/StarterContent/Textures/T_Burst_M"), LOAD_None);
LoadObject —— Outer
UPackage* TestPackage = LoadPackage(nullptr, TEXT("/Game/StarterContent/Textures/T_Burst_M"), LOAD_None);
UTexture2D* TestTexture2D = LoadObject<UTexture2D>(TestPackage,TEXT("T_Burst_M"));
LoadClass —— no Outer
TSubclassOf<AActor> TestClass = LoadClass<AActor>(nullptr, TEXT("Blueprint'/Game/Blueprint/BP_Test.BP_Test_C'"));
LoadClass —— Outer
UPackage* TestPackageTwo = LoadPackage(nullptr, TEXT("/Game/Blueprint/BP_Test"), LOAD_None);
TSubclassOf<AActor> TestClass = LoadClass<AActor>(TestPackageTwo, TEXT("BP_Test_C'"));
图中所涉及的其它同步加载使用方式,可参考[UE C++] 资源加载(一) 硬&软引用加载资源