首页 > 系统相关 >[UE4]资源异步加载(Assets Asynchronous Loading)与内存释放(Free Memory)

[UE4]资源异步加载(Assets Asynchronous Loading)与内存释放(Free Memory)

时间:2023-06-02 12:14:26浏览次数:51  
标签:异步 Loading Assets Game Free FSoftObjectPath AssetLoader 加载

为什么需要异步加载资源,因为当一次性加载的资源较多或者单个资源较大时,普通的LoadObject()方式会阻塞引擎的主线程。

 

假设测试工程叫TestTD4,自定义Character叫ATestTD4Character(头文件为TestTD4Character.h)

假设在Content/Assets/目录下放了三个动画文件(AnimSequence)。

异步加载

通过DefaultGame.ini配置文件生成FSoftObjectPath

DefaultGame.ini

[/Script/TestTD4.TestTD4Character]
+TestAssets=/Game/Assets/ThirdPerson_Jump.ThirdPerson_Jump
+TestAssets=/Game/Assets/ThirdPersonRun.ThirdPersonRun
+TestAssets=/Game/Assets/ThirdPersonWalk.ThirdPersonWalk

TestTD4Character.h

UPROPERTY(Config)
        TArray<FSoftObjectPath> TestAssets;

这个属性的意思是:加Config标签表示从DefaultGame.ini读取,TestAssets就是DefaultGame.ini中配置的属性名,当游戏启动时,这个数组会被自动填充3个元素,即资源的路径。

注:4.18版本中FStringAssetReference、TAssetPtr两个变量被重命名为:FSoftObjectPath、TSoftObjectPtr.

TestTD4Character.cpp

void ATestTD4Character::BeginPlay()
{
    Super::BeginPlay();

    for (FSoftObjectPath& Asset : TestAssets)
    {
        GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Cyan, Asset.ToString());
    }

    FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
    AssetLoader.RequestAsyncLoad(TestAssets, FStreamableDelegate::CreateUObject(this, &ATestTD4Character::AnimAssetsDeferred));
}

void ATestTD4Character::AnimAssetsDeferred()
{
    for (FSoftObjectPath SoftObj : TestAssets)
    {
        TAssetPtr<UAnimSequence> AnimAsset(SoftObj);

        UAnimSequence* AnimObj = AnimAsset.Get();
        if (AnimObj)
        {
            GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Red, AnimObj->GetName());
        }
    }
}

打印结果:

ThirdPersonWalk
ThirdPersonRun
ThirdPerson_Jump
/Game/Assets/ThirdPersonWalk.ThirdPersonWalk
/Game/Assets/ThirdPersonRun.ThirdPersonRun
/Game/Assets/ThirdPerson_Jump.ThirdPerson_Jump

如果注掉RequestAsyncLoad,直接执行AnimAssetsDeferred(),则打印结果为:

/Game/Assets/ThirdPersonWalk.ThirdPersonWalk
/Game/Assets/ThirdPersonRun.ThirdPersonRun
/Game/Assets/ThirdPerson_Jump.ThirdPerson_Jump

说明这三个资源确实是运行时异步加载,而不是游戏启动时就被自动加载。

 

通过路径字符串生成FSoftObjectPath

头文件中定义一个变量

FSoftObjectPath SoftObj;

在cpp函数中通过路径赋值(比如在BeginPlay()函数中)

SoftObj = FSoftObjectPath(TEXT("/Game/Mannequin/Animations/ThirdPersonWalk.ThirdPersonWalk"));
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
AssetLoader.RequestAsyncLoad(SoftObj, FStreamableDelegate::CreateUObject(this, &AUMGTestGameModeBase::AnimAssetsDeferred));

回调函数中获取对象

void ATestTD4Character::AssetsDeferred()
{
    TAssetPtr<UAnimSequence> WidgetAsset(SoftObj);
    UAnimSequence* AnimObj= WidgetAsset.Get();
    if (AnimObj)
    {
        GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Red, AnimObj->GetName());
    }
}
注意:UserWidget蓝图无法通过上述方式执行异步加载,目前只测试了Animation、Mesh等资源是可行的。

同步加载

同步加载有两种API:

  • FStreamableManager::LoadSynchronous
UAnimSequence* AimObj = AssetLoader.LoadSynchronous<UAnimSequence>(FSoftObjectPath(TEXT("/Game/Assets/ThirdPerson_Jump.ThirdPerson_Jump")));

 

  • FStreamableManager::RequestSyncLoad
TSharedPtr<FStreamableHandle> Handle = AssetLoader.RequestSyncLoad(FSoftObjectPath(TEXT("/Game/Assets/ThirdPerson_Jump.ThirdPerson_Jump")));
if (Handle.IsValid())
{
    UAnimSequence* AnimiObj = Cast<UAnimSequence>(Handle->GetLoadedAsset());
}
FStreamableManager的源码注释已经写明:RequestAsyncLoad、RequestSyncLoad、LoadSynchronous等待延迟时间可能长达数秒。LoadSynchronous和RequestSyncLoad的内部实现是对异步加载的封装:调用FStreamableHandle::WaitUntilComplete()阻塞等待。RequestSyncLoad函数内部要么会进行异步载入并且调用WaitUntilComplete函数,要么直接调用LoadObject函数 —— 哪个更快就调哪个。

 

加载蓝图(角色蓝图、UMG蓝图等)

StreamManager加载蓝图时,不能使用默认路径(即使后缀加_C),否则加载出来的Class无法使用,虽然不为空。
正确方式:前缀统一用Class,然后后缀再加_C。

示例:

FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
UClass* WidgetClass = AssetLoader.LoadSynchronous<UClass>(FSoftObjectPath("Class'/Game/TopDownCPP/Blueprints/NewWidgetBlueprint.NewWidgetBlueprint_C'"));
if (WidgetClass)
{
    UUserWidget* Widget = CreateWidget<UUserWidget>(this, WidgetClass);
if (Widget)
    {
        Widget->AddToViewport();
    }
}

 

资源内存释放

用上述方式加载资源后(包括同步加载和异步加载),如何再释放资源并从内存中销毁?两种情况:

 

自动回收

只要对象失去引用后就会被自动释放,无需手动Destroy。如果是异步加载,对象只在回调函数中有效,回调函数执行完毕后,就会被标记为可收回状态,如果此时ForceGC,则对象会立即销毁。

 

手动回收

在执行加载时,将bManageActiveHandle标记为true,默认为false:表示是否手动管理FStreamableHandle。如果设置为true,则对象会一直常驻内存直到手动释放。

FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
UParticleSystem* AimObj = AssetLoader.LoadSynchronous<UParticleSystem>(FSoftObjectPath(AssetPath), true);

当对象不再需要时,再手动执行执行Unload。之后对象就会被自动回收:

FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
AssetLoader.Unload(FSoftObjectPath(AssetPath));

Unload之后如果需要立即回收,可以执行ForceGC:

GEngine->ForceGarbageCollection(true);

在编辑器模式下,上述两种回收方式都不起效,会一直常驻内存,只有在打包运行版本中才会生效。如果在编辑器运行模式下强制Destroy()或者MarkPendingKill(),则对象可以从内存中销毁,但是无法再次Load,除非重启编辑器。

 

注意事项

  • FStreamableManager::Unload()会Release掉和当前资源相关的所有FStreamableHandle。比如在三处位置加载了同一个资源,即使bManageActiveHandle设置为true,那么只要调用Unload一次,就可以将这三个FStreamableHandle对象全部Release掉,即从内存中释放该对象;如果对这3个FStreamableHandle对象分别执行Release,那么只有当最后一个Handle被Release之后,该资源才会从内存中释放。
  • 异步加载时,谁先请求则谁的回调函数先执行,不会存在回调函数执行顺序乱序的问题(除非修改TAsyncLoadPriority),因为引擎内部接收回调请求的容器使用的是TArray,且每次执行索引为0的回调,然后RemoveAt(0)。
  • 异步加载时,如果资源还没加载完成就执行ReleaseHandle()(假设加载时bManageActiveHandle为true),比如在执行回调函数之前执行ReleaseHandle,那么当资源加载完成后(回调函数执行之后),会自动从内存中回收。不过该对象在回调函数中仍然有效,除非在回调函数内ForceGC。
void AMyPlayerController::TestAsyncLoadAndRelease()
{
    FStreamableDelegate Call;
    Call.BindUFunction(this, FName("AsyncLoadCallback"));

    FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
    Handle = AssetLoader.RequestAsyncLoad(FSoftObjectPath(AssetPath), Call, 0, true);
    Handle->ReleaseHandle();
}
  • UPROPERTY()修饰的成员变量,可以让其保持的资源对象常驻内存,如果不再需要驻留内存,将该成员变量值为NULL,等到下次GC时就会被自动回收
  • GEngine->ForceGarbageCollection();执行后,内存回收至少要等到下一帧才会执行。在当前帧内,即使一个对象执行ConditionalBeginDestroy()且执行了ForceGarbageCollection,当前帧内该对象仍然有效。
  • ConditionalBeginDestroy()是所有UObject都有的API,其对象销毁是异步执行且对象在当前帧内持续有效;AActor::Destroy()是AActor特有的API,其对象回收发生在当前帧结束时。

参考

《Fortnite》开发经验分享之运行时资源管理:Runtime Asset Management

https://answers.unrealengine.com/storage/temp/136465-runtimeassetmanagementin416.pdf​answers.unrealengine.com/storage/temp/136465-runti   转载自:https://zhuanlan.zhihu.com/p/33303645

标签:异步,Loading,Assets,Game,Free,FSoftObjectPath,AssetLoader,加载
From: https://www.cnblogs.com/Shaojunping/p/17451356.html

相关文章

  • freemarker模板分页处理
    借鉴博客:https://www.cnblogs.com/zhouyu629/p/12433259.html  1、创建一个分页页面:page.html,里面只有分页的内容<#macrofpagepagepagesizetotalpagestotalrecordsurl><li><span>共${totalrecords}条记录&nbsp;&nbsp;第${page}页/共${totalpages}页</span&......
  • go语言笔记——defer作用DB资源等free或实现调试
    defer和追踪关键字defer允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。关键字defer的用法类似于面向对象编程语言Java和C#的 fi......
  • 2018ACM浙江省赛 ZOJ 4029 Now Loading!!!(二分)
    NowLoading!!!TimeLimit: 1Second     MemoryLimit: 131072KBDreamGridhas  integers .DreamGridalsohas foragivennumber ,where , .InputTherearemultipletestcases.Thefirstlineofinputisaninteger Thefirstlinecon......
  • FreeSWITCH添加自定义endpoint
    操作系统:CentOS7.6_x64   FreeSWITCH版本:1.10.9 日常开发过程中会遇到需要扩展FreeSWITCH对接其它系统的情况,这里记录下编写FreeSWITCH自定义endpoint的过程。一、模块定义函数使用FreeSWITCH自带的框架来定义模块函数,函数指针及参数列表定义如下(src/include/switc......
  • 推荐20个Free图片素材网站
    以下是20个提供免费图片素材的网站,希望能帮到您:Unsplash(https://unsplash.com/)Unsplash是一个免费高分辨率照片分享平台。该网站上的照片由许多摄影师上传,每10天更新一次,授权使用时无需注明来源或提供署名,可以自由使用。Pexels(https://www.pexels.com/)Pexels是一个免费高质量图......
  • React 全局Loading
    import{Spin}from'antd'importReactDOMfrom'react-dom/client'importtype{SpinProps}from'antd'classLoading{privatecontainer=document.createElement('div')privateroot=ReactDOM.createRoot(this.co......
  • Freemarker模板语法大全
    FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化插值:#{expr}或#{expr;format}${book.name?if_exists}//用于判断如果存在,就输出这个值${book.name?default(‘xxx’)}//默认值xxx${book.name!"xxx"}//默认值xxx${book.date?string('yyyy-MM-dd')}//......
  • C++-double free or corruption(fasttop)
    C++-doublefreeorcorruption(fasttop)出现doublefreeorcorruption(fasttop)检查:delete,是否有重复delete隐式的复制构造函数导致析构次数增加全局变量,项目代码合并时,不同的共享库中出现同名的全局变量[本次问题的原因,修改新增的全局变量名称后fixed.]......
  • Unity2018.2 Standard Assets汉化
    下载中文汉化包拷贝到安装盘:\ProgramFiles\Unity\Editor\Data\Localization下面2018.1+的StandardAssets安装方法“自从我升级到2018.2之后,就再也找不到Unity自带的那些标准资源了,就是那个StandardAssets,里面有好多东西是我的必备资源呢,比如地形的那些贴图,第一人称控制......
  • FreeRTOS 信号量
    二值信号量  二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。  和队列一样,信号量API函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致......