当前位置: 首页 > 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…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG

TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码&#xff1a;HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...

6.9-QT模拟计算器

源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...