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

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例

目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码&#xff1a;冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践&#xff0c;很多人以为AI已经强大到不需要程序员了&#xff0c;其实不是&#xff0c;AI更加需要程序员&#xff0c;普通人…...