当前位置: 首页 > news >正文

给UE5优化一丢丢编辑器性能

背后的原理

先看FActorIterator的定义

/*** Actor iterator* Note that when Playing In Editor, this will find actors only in CurrentWorld*/
class FActorIterator : public TActorIteratorBase<FActorIterator>
{//.....
}

找到基类TActorIteratorBase

/*** Template class used to filter actors by certain characteristics*/
template <typename Derived>
class TActorIteratorBase
{
public:/*** Iterates to next suitable actor.*/void operator++(){// Use local version to avoid LHSs as compiler is not required to write out member variables to memory.AActor*           LocalCurrentActor      = nullptr;int32             LocalIndex             = State->Index;TArray<UObject*>& LocalObjectArray       = State->ObjectArray;TArray<AActor*>&  LocalSpawnedActorArray = State->SpawnedActorArray;const UWorld*     LocalCurrentWorld      = State->CurrentWorld;while(++LocalIndex < (LocalObjectArray.Num() + LocalSpawnedActorArray.Num())){if (LocalIndex < LocalObjectArray.Num()){LocalCurrentActor = static_cast<AActor*>(LocalObjectArray[LocalIndex]);}else{LocalCurrentActor = LocalSpawnedActorArray[LocalIndex - LocalObjectArray.Num()];}State->ConsideredCount++;ULevel* ActorLevel = LocalCurrentActor ? LocalCurrentActor->GetLevel() : nullptr;if ( ActorLevel&& static_cast<const Derived*>(this)->IsActorSuitable(LocalCurrentActor)&& static_cast<const Derived*>(this)->CanIterateLevel(ActorLevel)&& ActorLevel->GetWorld() == LocalCurrentWorld){// ignore non-persistent world settingsif (ActorLevel == LocalCurrentWorld->PersistentLevel || !LocalCurrentActor->IsA(AWorldSettings::StaticClass())){State->CurrentActor = LocalCurrentActor;State->Index = LocalIndex;return;}}}State->CurrentActor = nullptr;State->ReachedEnd = true;}
//.....省略剩余代码
}

可以看到TActorIteratorBase基本就是在遍历FActorIteratorState里的两个数组。为啥有两个数组?因为遍历的过程中可能又Spawn了新的Actor。
接着看FActorIteratorState的代码

/*-----------------------------------------------------------------------------Iterator for the editor that loops through all selected actors.
-----------------------------------------------------------------------------*//*** Abstract base class for actor iteration. Implements all operators and relies on IsActorSuitable* to be overridden by derived class.* Note that when Playing In Editor, this will find actors only in CurrentWorld.*/
class FActorIteratorState
{
public:/** Current world we are iterating upon						*/const UWorld* CurrentWorld;/** Results from the GetObjectsOfClass query				*/TArray<UObject*> ObjectArray;/** index of the current element in the object array		*/int32 Index;/** Whether we already reached the end						*/bool	ReachedEnd;/** Number of actors that have been considered thus far		*/int32		ConsideredCount;/** Current actor pointed to by actor iterator				*/AActor*	CurrentActor;/** Contains any actors spawned during iteration			*/TArray<AActor*> SpawnedActorArray;/** The class type we are iterating, kept for filtering		*/UClass* DesiredClass;/** Handle to the registered OnActorSpawned delegate		*/FDelegateHandle ActorSpawnedDelegateHandle;/*** Default ctor, inits everything*/FActorIteratorState(const UWorld* InWorld, const TSubclassOf<AActor> InClass) :CurrentWorld( InWorld ),Index( -1 ),ReachedEnd( false ),ConsideredCount( 0 ),CurrentActor(nullptr),DesiredClass(InClass){check(IsInGameThread());check(CurrentWorld);#if WITH_EDITOR// In the editor, you are more likely to have many worlds in memory at once.// As an optimization to avoid iterating over many actors that are not in the world we are asking for,// if the filter class is AActor, just use the actors that are in the world you asked for.// This could be useful in runtime code as well if there are many worlds in memory, but for now we will leave// it in editor code.if (InClass == AActor::StaticClass()){// First determine the number of actors in the world to reduce reallocations when we append them to the array below.int32 NumActors = 0;for (ULevel* Level : InWorld->GetLevels()){if (Level){NumActors += Level->Actors.Num();}}// Presize the arrayObjectArray.Reserve(NumActors);// Fill the arrayfor (ULevel* Level : InWorld->GetLevels()){if (Level){ObjectArray.Append(Level->Actors);}}}else
#endif // WITH_EDITOR{constexpr EObjectFlags ExcludeFlags = RF_ClassDefaultObject;GetObjectsOfClass(InClass, ObjectArray, true, ExcludeFlags, EInternalObjectFlags::Garbage);}const auto ActorSpawnedDelegate = FOnActorSpawned::FDelegate::CreateRaw(this, &FActorIteratorState::OnActorSpawned);ActorSpawnedDelegateHandle = CurrentWorld->AddOnActorSpawnedHandler(ActorSpawnedDelegate);}~FActorIteratorState(){CurrentWorld->RemoveOnActorSpawnedHandler(ActorSpawnedDelegateHandle);}/*** Returns the current suitable actor pointed at by the Iterator** @return	Current suitable actor*/FORCEINLINE AActor* GetActorChecked() const{check(CurrentActor);checkf(!CurrentActor->IsUnreachable(), TEXT("%s"), *CurrentActor->GetFullName());return CurrentActor;}private:void OnActorSpawned(AActor* InActor){if (InActor->IsA(DesiredClass)){SpawnedActorArray.AddUnique(InActor);}}
};

着重看红色框中的代码
在这里插入图片描述

可以看到在Editor下当InClass是AActor::StaticClass()的时候,会遍历当前World中所有的Actor。

再看FActorIterator的构造函数
在这里插入图片描述

可以看到FActorIterator只传一个构造参数的时候,InClass会默认是AActor::StaticClass(),也就会遍历场景中所有的Actor。当我们明确知道自己想要遍历的是Actor子类时,却因为少传了一个参数而被迫轮询了一遍所有Actor!

再来看ForEachObjectOfClass的代码。

void ForEachObjectOfClass(const UClass* ClassToLookFor, TFunctionRef<void(UObject*)> Operation, bool bIncludeDerivedClasses, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags)
{TRACE_CPUPROFILER_EVENT_SCOPE(ForEachObjectOfClass);// Most classes searched for have around 10 subclasses, some have hundredsTArray<const UClass*, TInlineAllocator<16>> ClassesToSearch;ClassesToSearch.Add(ClassToLookFor);FUObjectHashTables& ThreadHash = FUObjectHashTables::Get();FHashTableLock HashLock(ThreadHash);if (bIncludeDerivedClasses){RecursivelyPopulateDerivedClasses(ThreadHash, ClassToLookFor, ClassesToSearch);}ForEachObjectOfClasses_Implementation(ThreadHash, ClassesToSearch, Operation, ExclusionFlags, ExclusionInternalFlags);
}

RecursivelyPopulateDerivedClasses是查找ClassToLookFor所有的子类。
ForEachObjectOfClasses_Implementation则是在遍历所有这些子类的实例对象。代码如下:

FORCEINLINE void ForEachObjectOfClasses_Implementation(FUObjectHashTables& ThreadHash, TArrayView<const UClass* const> ClassesToLookFor, TFunctionRef<void(UObject*)> Operation, EObjectFlags ExcludeFlags /*= RF_ClassDefaultObject*/, EInternalObjectFlags ExclusionInternalFlags /*= EInternalObjectFlags::None*/)
{TRACE_CPUPROFILER_EVENT_SCOPE(ForEachObjectOfClasses_Implementation);// We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading.ExclusionInternalFlags |= UE::GC::GUnreachableObjectFlag;if (!IsInAsyncLoadingThread()){ExclusionInternalFlags |= EInternalObjectFlags::AsyncLoading;}TBucketMapLock ClassToObjectListMapLock(ThreadHash.ClassToObjectListMap);for (const UClass* SearchClass : ClassesToLookFor){auto List = ThreadHash.ClassToObjectListMap.Find(SearchClass);if (List){for (auto ObjectIt = List->CreateIterator(); ObjectIt; ++ObjectIt){UObject* Object = static_cast<UObject*>(*ObjectIt);if (!Object->HasAnyFlags(ExcludeFlags) && !Object->HasAnyInternalFlags(ExclusionInternalFlags)){if (UE::GC::GIsIncrementalReachabilityPending){UE::GC::MarkAsReachable(Object);}Operation(Object);}}}}
}

解决方法及建议

  • 对于Actor子类的遍历
    建议使用TActorIterator。示例如下:
for (TActorIterator<AInstancedFoliageActor> It(InWorld); It; ++It)
{AInstancedFoliageActor* IFA = *It;if (IFA->GetLevel() == InLevel){IFA->PostApplyLevelOffset(InOffset, bWorldShift);}
}
  • 对于UActorComponent或UObject子类
    建议使用ForEachObjectOfClass或者GetObjectsOfClass。示例如下:
TArray<UWorld*, TInlineAllocator<4>> WorldsToEOFUpdate;
ForEachObjectOfClass(UWorld::StaticClass(), [&WorldsToEOFUpdate](UObject* WorldObj)
{UWorld* World = CastChecked<UWorld>(WorldObj);if (World->HasEndOfFrameUpdates()){WorldsToEOFUpdate.Add(World);}
});
  • UGameplayStatic一众方法
    推荐使用UGameplayStatics::GetAllActorsOfClassWithTag、UGameplayStatics::GetAllActorsOfClass,尽量避免使用UGameplayStatics::GetAllActorsWithTag、UGameplayStatics::GetAllActorsWithInterface。

赶紧去检查自己项目有没有类似问题吧!

引擎代码提交

提交的PR,部分已经Merge到官方Github了。

  • FBehaviorTreeDebugger https://github.com/EpicGames/UnrealEngine/pull/12608
  • FUsdStageModule https://github.com/EpicGames/UnrealEngine/pull/12612
  • SNiagaraBaselineViewport https://github.com/EpicGames/UnrealEngine/commit/acbe3976d6083f09fc6c7f0e804013c91ad8060c
  • FControlRigEditMode https://github.com/EpicGames/UnrealEngine/pull/12611
  • FReplayHelper https://github.com/EpicGames/UnrealEngine/pull/12614
  • LevelEditorActions.cpp https://github.com/EpicGames/UnrealEngine/pull/12615
  • UReflectionCaptureComponent https://github.com/EpicGames/UnrealEngine/pull/12616

相关文章:

给UE5优化一丢丢编辑器性能

背后的原理 先看FActorIterator的定义 /*** Actor iterator* Note that when Playing In Editor, this will find actors only in CurrentWorld*/ class FActorIterator : public TActorIteratorBase<FActorIterator> {//..... }找到基类TActorIteratorBase /*** Temp…...

【Docker】常用命令汇总

Docker 是1个开源的应用容器引擎&#xff0c;基于Go 语言并遵从 Apache2.0 协议开源。 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全使用沙箱机制&#xff0c;相…...

Mybatis:CRUD数据操作之多条件查询及动态SQL

Mybatis基础环境准备请看&#xff1a;Mybatis基础环境准备 本篇讲解Mybati数据CRUD数据操作之多条件查询 1&#xff0c;编写接口方法 在 com.itheima.mapper 包写创建名为 BrandMapper 的接口。在 BrandMapper 接口中定义多条件查询的方法。 而该功能有三个参数&#xff0c;…...

【笔记】轻型民用无人驾驶航空器安全操控

《轻型民用无人驾驶航空器安全操控》 理论考试培训材料 法规部分 【民用无人驾驶航空器的分类】 1、如何定义微型、轻型无人驾驶航空器&#xff1f; 微型无人驾驶航空器&#xff0c;是指空机重量小于0.25千克&#xff0c;最大平飞速度不超过40千米/小时&#xff0c;无线电发…...

TouchGFX设计模式代码实例说明

一)Model - View - Presenter (MVP) 模式在 TouchGFX 中的应用 1)Model&#xff08;模型&#xff09;&#xff1a; 模型代表应用程序的数据和业务逻辑。例如&#xff0c;在一个简单的计数器应用中&#xff0c;模型可以是一个包含计数器当前值的类。 class CounterModel { pri…...

flink学习(7)——window

概述 窗口的长度(大小): 决定了要计算最近多长时间的数据 窗口的间隔: 决定了每隔多久计算一次 举例&#xff1a;每隔10min,计算最近24h的热搜词&#xff0c;24小时是长度&#xff0c;每隔10分钟是间隔。 窗口的分类 1、根据window前是否调用keyBy分为键控窗口和非键控窗口…...

restTemplate get请求

报错解释&#xff1a; 这个报错信息表明在使用RestTemplate进行GET请求时&#xff0c;需要提供一个请求类型&#xff08;reqType&#xff09;&#xff0c;但是传入的值为空。这通常意味着在构建请求或者调用方法时&#xff0c;没有正确设置请求的Content-Type头部&#xff0c;…...

ffmpeg 预设的值 加速

centos 安装ffmpeg 编译安装 官网获取最新的linux ffmpeg 代码 https://ffmpeg.org//releases/ mkdir -p /data/app/ffmpeg cd /data/app/ffmpeg wget http://www.ffmpeg.org/releases/ffmpeg-7.1.tar.gz tar -zxvf ffmpeg-7.1.tar.gz#安装所需的编译环境 yum install -y \…...

maven <scope>compile</scope>作用

在 Maven 项目中&#xff0c; 元素用于定义依赖项的作用范围。 元素可以有多个值&#xff0c;每个值表示不同的作用范围。其中&#xff0c;scope compile scope 是默认的作用范围&#xff0c;表示该依赖项在编译、测试和运行时都需要。 scope compile scope 的含义 1、编译时…...

Ubuntu Server 22.04.5 从零到一:详尽安装部署指南

文章目录 Ubuntu Server 22.04.5 从零到一&#xff1a;详尽安装部署指南一、部署环境二、安装系统2.1 安装2.1.1 选择安装方式2.1.2 选择语言2.1.3 选择不更新2.1.4 选择键盘标准2.1.5 选择安装版本2.1.6 设置网卡2.1.7 配置代理2.1.8 设置镜像源2.1.9 选择装系统的硬盘2.1.10 …...

反射机制了解

反射概念 了解反射背景 存在某些变量或形参的声明类型是Object类型&#xff0c;但是程序却需要调用该对象运行时类型的方法&#xff0c;该方法不是Object中的方法&#xff0c;如何解决。转到如何获取该对象运行时类型的方法。 只能运行时才能获取&#xff0c;这就用到反射。 …...

机器学习策略Ⅰ

机器学习策略Ⅰ 在构建一个好的监督学习系统时&#xff0c;通常需要确保以下四个方面&#xff1a; 系统需要在训练集上能够很好地拟合数据&#xff0c;达到某种可接受的性能水平&#xff08;如接近人类水平&#xff09;。如果训练集表现不好&#xff0c;可以使用更大的模型&…...

redis中的bigkey及读取优化

一、bigKey介绍 1、简介 在 Redis 中,Big Key(大键)指的是占用大量内存的单个键。通常,Redis 是一个高性能的内存数据库,但是当某些键变得非常大时,会带来性能上的影响。例如,大量的内存消耗、长时间的操作延迟,甚至可能导致 Redis 停止响应或崩溃。 通俗的来说,指…...

【西瓜书】支持向量机(SVM)

支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;。 超平面 分类学习最基本的想法就是基于训练集合D在样本空间中找到一个划分超平面&#xff0c;将不同类别的样本分开。 但能将训练样本分开的划分超平面可能有很多&#xff0c;应该努力去找到哪…...

三维渲染中顺序无关的半透明混合(OIT)(二——Stencil Route)

1、A-Buffer算法。 在谈到Stencil Route之前&#xff0c;需要先讨论A-Buffer算法。A-Buffer是一种图形学&#xff08;渲染方向&#xff09;上的用于可见面分析(Visble Surface Detection)的技术&#xff0c;是Z-Buffer的衍生方法。 Z-Buffer是用于剔除 不透明 物体的算法。假…...

(SAST检测规则-3)固定的 SessionID 缺陷详解

漏洞类型&#xff1a; 会话固定攻击&#xff08;Session Fixation Attack&#xff09; 漏洞描述&#xff1a; 会话固定攻击是利用服务器的会话管理机制存在漏洞&#xff0c;攻击者通过提前控制或预测用户的会话标识符&#xff08;Session ID&#xff09;&#xff0c;当用户登录…...

【安卓开发】【Android Studio】项目构建(Build)时报错:Integer Overflow

一、问题描述 在安卓项目中&#xff0c;构建&#xff08;Build&#xff09;失败并报错&#xff1a;xxxxx Integer Overflow&#xff08;整型溢出&#xff09;。 二、相关代码 刚开始以为是某个整数&#xff08;例如控件、java类&#xff09;不匹配造成的&#xff0c;检查如下…...

STM32主要功能

STM32 是由意法半导体&#xff08;STMicroelectronics&#xff09;推出的一系列基于 ARM Cortex-M 内核的微控制器&#xff08;MCU&#xff09;。STM32 微控制器广泛应用于嵌入式系统中&#xff0c;因其高性能、低功耗、丰富的外设接口和多种封装形式而被广泛采用。其主要功能和…...

MacOS 如何连接 Linux NFS 服务器

以 Ubuntu 为例。 Ubuntu 服务器端设置 1. 进入 root 权限&#xff0c;安装 NFS 服务&#xff1a; apt-get update apt-get install nfs-kernel-server2. 创建共享目录&#xff1a; mkdir /data chown nobody:nogroup /data chmod 777 /data3. 配置 /etc/exports 文件: vi …...

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-39

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

【Veristand】Veristand环境安装教程-Linux RT / Windows

首先声明&#xff0c;此教程是针对Simulink编译模型并导入Veristand中编写的&#xff0c;同时需要注意的是老用户编译可能用的是Veristand Model Framework&#xff0c;那个是历史版本&#xff0c;且NI不会再维护&#xff0c;新版本编译支持为VeriStand Model Generation Suppo…...