- 概述
- 调试内存:步骤
- 内存跟踪工具与技术
- STAT LEVELS
- DVD 空间占用
- 可执行文件大小
- 有规律加载的内容
- 内容成本比较
- 引擎内存池
- 热点报告
- MemLeakCheck Tracking(内存泄露检测追踪)
- 贴图池
- TArray 闲置
- 内存碎片
- 栈内存
- 查找引用的资源
- 游戏控制台平台的内存
概述
内存使用通常与视频游戏密切相关,尤其是那些适用于内存有限的控制台或移动设备的视频游戏。它会从磁盘上内容资源占据的空间量扩展为运行时过程中不同系统的内存使用量。这些全部都是极其重要的信息,它们可以将内存使用限制在可接受范围内。
调试内存:步骤
1. 加载入戏
如果您由于内存不足而不能加载游戏,请参照关卡优化页面来学习如何优化关卡。
通常有一个已定的内存预算指导方针是个好主意–比如,在关卡中可以使用多少静态网格物体;或者使用多少其它的资源比如骨架网格物体、动画、声效。这些都可以根据您做的是什么游戏以及它们是关于什么内容的而动态地变化。
2. 运行游戏并观察
即使游戏运行了,也不意味着您没有用光内存的危险。您需要知道您的游戏现在所使用的内存量以及可以供新的内容和功能使用的内存量。
平台提供者有一些工具来完成这个功能。请参照下面的平台部分。
3. 降低内存使用量(没有OOM)
当您的内存不足时,您需要尽量减少可能造成这种现象的根源。
一些可能造成这种现象的原因包括:
- 具有太多静态网格物体的关卡。
- 使用了太多射弹和粒子的关卡。
- 具有占用了太多内存的代码。
基本理解
关于在具体位置应该使用多少内存没有通用答案。
根据您的游戏,计算出哪里要占用内存。
所以您需要了解您当前的内存使用情况以及您的内存预算情况。
- Budget(预算): 决定把内存应用在哪里
- 确保所有的资源都是最佳的,并且没有任何不必要的引用。
- 确保您有足够的缓冲(用于碎片)来保持您游戏的运行状态。
内存跟踪工具与技术
内存跟踪工具
现有的各种工具和调试技术对于跟踪您游戏的内存泄漏问题和帮助查找内存分配状况趋势都是非常有用的。内存跟踪页面主要涉及这些工具中的一些,同时详细描述了如何使用它们跟踪您的游戏内存问题。
请参阅(内存使用)页面了解有关查找过度使用内存的实例作为战争机器3中的案例研究的详细信息。
内存分析器
内存分析器是一个可以追踪您的游戏中的内存具体分配情况的特定工具,但是运行时速度会因为它受到影响。它被分割为两部分独立的代码:引擎中的C++后端和用于查看数据的C#前端。
要了解有关您的游戏使用内存分析器的详细信息,请参阅内存分析器页面。
STAT LEVELS
可以使用 STAT LEVELS
命令查看内存中加载的关卡数。
- 红色 -关卡已加载并且可见。
- 橘黄色 -正在使关卡可见的过程中。
- 黄色 -关卡已加载但不可见。
- 蓝色 -关卡没有被加载,但是处在内存中,当发生垃圾回收时,它将会被清除。
- 绿色 -关卡没有被加载。
- 紫色 - 正在预加载关卡。
如果你有一些不必加载的关卡,那么请一定不要加载它。如果可能,黄色的关卡是不需要进行加载及推迟加载的最好备选项。如果您有一个大的动态载入的关卡,请尝试把它进行分隔或者进行优化。
STAT MEMORY
STAT MEMORY
命令可以提供关卡中资源的内存使用情况的基本要点。
- Audio Memory(音频内存)
- Novodex Allocation(Novodex内存分配)
- 动画
- Vertex Lighting(顶点光照)
- StaticMesh Vertex/Index(静态网格物体 顶点/索引)
- SkeletalMesh Vertex/Index(骨架网格物体 顶点/索引)
- Decal Vertex/Index(Decal 顶点/索引)
- VertexShader(顶点着色器)
- PixelShader(像素着色器)
- Texture Pool Size(贴图池大小)
- FaceFX
由于虚幻引用系统,以上这些很多都与加载游戏内容的大小相关,例如 Animation(动画)、SkeletalMesh(骨架网格物体)、Audio(音频)和 FaceFX。本文将会对这方面的内容进行详细说明。
DVD 空间占用
获得 DVD 上所放置的东西的高层次视图使得我们查看在哪里放置资源来降低空间占用量变得更加容易。是静态网格物体、着色器、贴图或者声效占用了大部分空间吗?
您将需要烘焙您的游戏并运行GAMENAME minecookedpackages命令。它将会使用烘焙的版本的数据更新DB(数据库)。然后由 UBaseDatabaseCommandlet 衍生而来的命令行开关会解压需要的数据来分析磁盘上的内容。例如,AnalyzeDVDSpace 和 FindCinematicTextures。使用基础数据库命令行开关中的框架,添加新的数据挖掘范式是无关紧要的。
可执行文件大小
您的可执行文件尺寸可以耗尽内存。您连接到可执行文件中的代码越多,您的.exe文件会变得越大。我们通常会忘记使用#ifdef来排除掉调试代码。我们也经常会把很多在游戏机平台上不需要的代码连接到可执行文件中。仅是靠查看源代码,找到这些情况中的任何一种都是非常困难的。所以我们需要使用工具来帮助我们完成这个操作。
这里是两个非常酷的工具,它可以帮助我们找出您的.exe文件中的东西。然后一旦您发现了不应该存在于可执行文件的东西,您便可以把它们删除掉! :-)
有规律加载的内容
总是要加载的内容是指Native脚本包中引用的任何东西或者明确地加入到AddToRoot()函数并从来没有移除的东西。基本上,不管游戏类型是什么,对于任何单独的地图来说这些内容都应该在内存中。所以,如果您有一个在整个游戏中仅出现在一张地图中的对象被Native脚本引用,那么每个单独的其它地图都也要加载这个对象。
您应该最小化这种内容的量。如果对于特定的游戏类型您有一些“常用”的对象,那么您可以使用一个CommonGameType对象来引用这些常用对象,当玩那个特定游戏类型时便加载这些对象。
有规律加载的内容页面描述如何查找和处理在不需要的时候有规律加载的内容。
内容成本比较
当您有很多内容时,在游戏中使用的对象通常都会有截然不同的内存消耗。通常这些大量不同具有副作用,即多人创建资源。或者是由于将“Boss Encounter(遭遇 Boss)”变更为只是与迷你 boss 相遇。随着时间发展事件不断发生变化。如果您没有跟踪并与其他人比较资源的方法,那么最终将造成资源使用不当。
从根本上说,我们希望使同样类型的 Weapon(武器)消耗的内存也大致相同。相同类型的角色消耗的内存也大致相同。这样做,可以使我们更容易地策划将要在没有特别包装所有资源的情况下工作的关卡。
GAMENAME ContentComparisonCommandlet
注意: 您可能希望这条指令每夜都可以运行
这会将资源的总内存分为“主要”类别数,然后进行更好的比较。
它们目前位于 BaseEngine.ini,内容如下:
[ContentComparisonReferenceTypes]
+Class=AnimSet(动画集)
+Class=SkeletalMesh(骨架网格物体)
+Class=SoundCue
+Class=StaticMesh(静态网格物体)
+Class=Texture2D(贴图 2D)
由于它们通常代表内存使用的主要情况,并且在资源中最容易进行比较,所以我们现在使用的就是这些属性。
例如,如果您持有 N 武器。您大致可以将它们分成: 小型、中型和大型武器。通常我们不会让小武器的消耗高过大武器。所以可以使每个武器消耗的内存形象化,这个方法非常好,可以确保我们消耗的内存符合规律。
总而言之,我们希望我们的资源类型可以真正做到低标准背离。示例: 如果我们拥有与 N 相似的角色,就 SkeletalMesh(骨架网格物体)而言,我们不希望一个角色消耗的内存比其他所有角色多出 1.5 到 2.0 倍。
一个角色最终会比其他资源消耗更多内存的原因很多。而且其中某些原因只是不知道而已。该命令开关的设计意图是列出消耗,然后确保它们与我们的预期设计相符。这里还会提供一个调查异常值的好方法。
要设置 ContentComparisionCommandlet
浏览的类,请修改 DefaultEditor.ini
ContentComparisonClassesToCompare
内容柱状图
假设您想知道总是需要加载的15兆的声效的情况。但是这些声效是什么哪? 如果您按照列表一个个地听它们,那么您需要听完成百个声效组成的巨大列表,通过这种方式来获得声效的分布状况是非常痛苦的。同样,当您需要加载成百个贴图时也发生了同样的问题。
所以,具有一个可以查看内存中的各种声音类型的高层次的视图便可以为您节约很多时间。如果百分比是正确的那么一切都可以工作的很好。但是,如果您发现您在脚步声效上消耗了您声效预算内存的 40%,那么这里给您提供了快速优化它们的地方。
通过结合使用 MemLeakCheck 和 xls 电子表格,您可以生成有效的柱状图。下面提供了可以用于声音和贴图的样本 .xls 文件: SoundTextureHistograms.xlsx
加载您的关卡,然后进行下面的操作:
- 运行 MemLeakCheck
- 复制并粘帖一系列声效到.xls的SoundData标签下。
- 复制并粘帖一系列贴图到.xls的TextureData 标签下。
- 然后.xls将会给出每个类别及其那个类别所使用的内存量。
注意: 如果您已经添加了 SoundClasses 或 TextureGroups,那么您将更新具有这些类别的 .xls 文件。
引擎内存池
当引擎加载新关卡并创建新对象时,就会有很多随着时间不断增加的对象池/缓冲/列表。它们将会在加载很多关卡之后趋于稳定。问题是它不会“覆盖”所有关卡,也不会常常“清理”所有关卡。所以您可以达到一些正常的“stat memory”类型记录中似乎没有的高水位线。其中有一些是通过调用 WorldInfo.ClearObjectPools 或根据数据池清除函数进行清除。
以下是一些对象池/缓冲/列表的示例:
- 名称表
- Octree
- RB_BodyInstance 对象池 (c.f. DISABLE_POOLED_RB_INSTANCES)
- RB_ConstraintInstance 对象池 (c.f. DISABLE_POOLED_RB_INSTANCES)
- Static Mesh Collision Scales(静态网格物体碰撞缩放)
- 有关渲染主题的 Static Draw(静态绘制)列表
- 使用 EmitterPool 的粒子
- 使用 DecalManager 的贴花
热点报告
热点报告可以用于辨别由于子关卡动态载入和载出而造成的内存减少。它们将允许您的 LD 更清楚地看到消耗最多内存的散列表元,并且应该允许他们优化他们的关卡使其满足内存需求。要获取创建和使用热点报告的完整指南,请参阅热点报告生成页面。
MemLeakCheck Tracking(内存泄露检测追踪)
MemLeakCheck提供了一个关于您的游戏所使用的内存状况的非常简单并容易理解的视图。它仅是一个文本文件,所以您可以您可以通过比较它们的不同来不断地查看它们的变化。
有关这项技术的更多详细信息,请参阅MemLeakCheck 追踪页面。
贴图池
贴图池的大小是固定的,所以当具有大量的贴图时不会对我们整个系统的内存产生任何负面影响。那也就是说我们想确保我们正在高效地使用那个内存。也就意味着我们需要确保那些我们可以看到的贴图所占的内存量在贴图池的大小之内。我们可以在整个地图上使用数G的贴图,只要我们周围的内存区域没有超出贴图池的大小即可。
使用 stat streaming
命令将会显示贴图动态载入统计数据。您所关心的统计数据是:
- 动态载入Fudge Factor(乏析因数): 我们想使得这个值尽可能地接近1.0f。
- Over Budget(超出预算): 我们想使得这个值尽可能地接近0f。
listtextures
控制台命令可以显示当前在池中的所有贴图及其大小。查看那个列表是您可以查看在贴图池中是否存在一些有问题的贴图!
在编辑器中有一个TextureStats(贴图统计数据)标签。在PIE中跑您的关卡,然后查看那个标签,您将会看到很多关于贴图的统计数据。其中一个非常重要的是"Last Seen(sec)(上一次看到贴图(秒))"栏。如果您有一个贴图即使被引用了,但是在那个栏中从来没有看得到它出现过,那么您应该查看哪里使用了它,或许可以删除它!
除了着色器目标之外的所有贴图都使用贴图池。这包括光照贴图。(使用光照贴图而不是顶点光照在内存节约上肯定是个胜利,因为它们将不会使系统内存发生变动。
同时,请确保您的游戏具有理想的贴图池大小。统计数据内存将会显示使用的贴图池大小。基于那些数据,您将可以决定您游戏所要使用的贴图池的最佳大小。
在 DefaultEngine.ini
中:
[TextureStreaming]
PoolSize=120
TArray 闲置
TArrays对于存储数据来说是很好的。问题是在默认的策略中TArray会有很多闲置的元素。另外,即使使用保守的闲置策略,您也会经常遇到那样TArray完成了获取新的数据但是仍然有闲置空间的情况。所以,我们想在任何可能的地方把闲置空间降低为零。
TArray 闲置可以通过启动 TRACK_ARRAY_SLACK
进行跟踪。
内存碎片
MEM STAT: 仅是概要总结。
MEM DETAILED:
这将会输出关于分配器的有用信息。请参阅下面的平台部分。
您需要保留大约一个缓冲的内存(根据平台而定)以便处理碎片问题。您的游戏将会在它达到报告释放内存的 "0" 之前由于内存溢出而崩溃。
栈内存
主线程继承于栈内存。尽管对于您创建的线程,您可以指定您的堆栈大小,如果您使用的是可以在内部创建线程的库,那么它可能不会考虑内存问题。
查找引用的资源
当游戏关卡已经接近完成时,对所有关卡进行一遍加载来确保它们仅加载了需要的资源是值得的。在虚幻引擎3中,任何被引用的内容都会被加载到游戏中,而且引用内容的方法–包括直接地和间接地引用等–这样您最终便会不知不觉地加载了很多的资源。比如,一个Pawn引用一个骨架网格物体,它将会自动地加载那个骨架网格物体所引用的动画集,从而那个动画集的所有动画(Pawn->Skeletalmesh->Animsets->Animations)将会被加载。从内存方面来说,这个链接关系是很危险的,因为游戏有很多Actors之间彼此进行着引用。
一般,您都必须要和关卡设计人员协同工作来决定是否有一些东西不必进行加载或者是否要对动态载入关卡进行分割以及是否不需要同时加载所有关卡。其中的一个实例许是在这个关卡中不需要出现的敌人网格物体。
MEMLEAKCHECK:
Memleakcheck
打印出许多基本游戏数据到一个文本格式的输出文件 [ProfileDirectory:Platform-specific]/MemLeak
中。
这将会至少为您显示骨架网格物体、动画集、声效等物体列表的简单概括信息。一旦您有了基本的信息,您便可以仔细地检查它们来查看是否有不需要加载的资源。如果是这样的,那么可以跟踪查出是谁引用了它,然后删除那个引用是主要的解决方式。
以下是案例学习...
OBJ LIST CLASS=SKELETALMESH:
列出了所有加载的SkeletalMeshes(骨架网格物体),按照占用内存的大小进行排序。MEMLEAKCHECK
包含这个信息,但是它是按照字母顺序排序的,这使得您很难找到要进行优化的内容。
LISTSOUNDS:
列出了所有加载的SoundCues,按照占用内存的大小进行排序。MEMLEAKCHECK
包含这个信息,但是它是按照字母顺序排序的,这使得您很难找到要进行优化的内容。
OBJ LIST CLASS=STATICMESH: 列出了所有加载的静态网格物体,按照占用内存的大小进行排序。
LISTANIMSETS:
列出了所有加载的SoundCues,按照占用内存的大小进行排序。MEMLEAKCHECK 包含这个信息。
OBJ REFS:
用来查找为什么加载某个资源的最好用的方法是使用 OBJ REFS
命令。要想使用这个命令,您需要在PC上运行游戏,因为由于递归函数的存在它需要一个较深的栈,而在UE3的游戏控制台平台版本上通常会崩溃的。
播放到加载有问题的资源的地方,然后输入: OBJ REFS CLASS
NAME= = 。
比如:
OBJ REFS CLASS=SKELETALMESH NAME=BIG_OGRE_2
然后它将会显示引入这个资源的引用物体链,比如:
Log: Shortest reachability from root to SkeletalMesh COG_Bolo_Grenade.Frag_Grenade:
Log: SkeletalMesh COG_Bolo_Grenade.Frag_Grenade [target] (root) (standalone)
Log: SkeletalMeshComponent GearGame.Default__GearProj_FragGrenade:COGGrenadeMesh0 (root) (ObjectProperty Engine.SkeletalMeshComponent:SkeletalMesh)
Log: Class GearGame.GearProj_FragGrenade (root) (standalone)
在上面的实例中,Frag_Grenade网格物体由native 类GearProj_FragGrenade的默认属性所引用。
注意可能会有不止一个引用,在这种情况下您必须一个一个地处理它们。
这里是一些我们找到的没有正确地加载资源的原因:
- 资源被native类引用。
- UnrealScript代码引用这些需要从中获得默认属性的类(该类引用了资源)。
Asset = class'MyGameContent.Pawn_BigOgre'.default.Mesh.PhysicsAsset;
- A Touch Kismet event (SeqEvt_Touch) references the class in its ClassProximityTypes or IgnoredClassProximityTypes arrays. Kismet的Touch事件(SeqEvt_Touch)引用它的ClassProximityTypes或IgnoredClassProximityTypes数组中的类。
- UnrealScript代码引用这些需要从中获得默认属性的类(该类引用了资源)。
游戏控制台平台的内存
- Xbox360: Xenon 内存调试
- PS3: PS3 内存调试
- 概述
- 调试内存:步骤
- 内存跟踪工具与技术
- STAT LEVELS
- DVD 空间占用
- 可执行文件大小
- 有规律加载的内容
- 内容成本比较
- 引擎内存池
- 热点报告
- MemLeakCheck Tracking(内存泄露检测追踪)
- 贴图池
- TArray 闲置
- 内存碎片
- 栈内存
- 查找引用的资源
- 游戏控制台平台的内存
概述
内存使用通常与视频游戏密切相关,尤其是那些适用于内存有限的控制台或移动设备的视频游戏。它会从磁盘上内容资源占据的空间量扩展为运行时过程中不同系统的内存使用量。这些全部都是极其重要的信息,它们可以将内存使用限制在可接受范围内。
调试内存:步骤
1. 加载入戏
如果您由于内存不足而不能加载游戏,请参照关卡优化页面来学习如何优化关卡。
通常有一个已定的内存预算指导方针是个好主意–比如,在关卡中可以使用多少静态网格物体;或者使用多少其它的资源比如骨架网格物体、动画、声效。这些都可以根据您做的是什么游戏以及它们是关于什么内容的而动态地变化。
2. 运行游戏并观察
即使游戏运行了,也不意味着您没有用光内存的危险。您需要知道您的游戏现在所使用的内存量以及可以供新的内容和功能使用的内存量。
平台提供者有一些工具来完成这个功能。请参照下面的平台部分。
3. 降低内存使用量(没有OOM)
当您的内存不足时,您需要尽量减少可能造成这种现象的根源。
一些可能造成这种现象的原因包括:
- 具有太多静态网格物体的关卡。
- 使用了太多射弹和粒子的关卡。
- 具有占用了太多内存的代码。
基本理解
关于在具体位置应该使用多少内存没有通用答案。
根据您的游戏,计算出哪里要占用内存。
所以您需要了解您当前的内存使用情况以及您的内存预算情况。
- Budget(预算): 决定把内存应用在哪里
- 确保所有的资源都是最佳的,并且没有任何不必要的引用。
- 确保您有足够的缓冲(用于碎片)来保持您游戏的运行状态。
内存跟踪工具与技术
内存跟踪工具
现有的各种工具和调试技术对于跟踪您游戏的内存泄漏问题和帮助查找内存分配状况趋势都是非常有用的。内存跟踪页面主要涉及这些工具中的一些,同时详细描述了如何使用它们跟踪您的游戏内存问题。
请参阅(内存使用)页面了解有关查找过度使用内存的实例作为战争机器3中的案例研究的详细信息。
内存分析器
内存分析器是一个可以追踪您的游戏中的内存具体分配情况的特定工具,但是运行时速度会因为它受到影响。它被分割为两部分独立的代码:引擎中的C++后端和用于查看数据的C#前端。
要了解有关您的游戏使用内存分析器的详细信息,请参阅内存分析器页面。
STAT LEVELS
可以使用 STAT LEVELS
命令查看内存中加载的关卡数。
- 红色 -关卡已加载并且可见。
- 橘黄色 -正在使关卡可见的过程中。
- 黄色 -关卡已加载但不可见。
- 蓝色 -关卡没有被加载,但是处在内存中,当发生垃圾回收时,它将会被清除。
- 绿色 -关卡没有被加载。
- 紫色 - 正在预加载关卡。
如果你有一些不必加载的关卡,那么请一定不要加载它。如果可能,黄色的关卡是不需要进行加载及推迟加载的最好备选项。如果您有一个大的动态载入的关卡,请尝试把它进行分隔或者进行优化。
STAT MEMORY
STAT MEMORY
命令可以提供关卡中资源的内存使用情况的基本要点。
- Audio Memory(音频内存)
- Novodex Allocation(Novodex内存分配)
- 动画
- Vertex Lighting(顶点光照)
- StaticMesh Vertex/Index(静态网格物体 顶点/索引)
- SkeletalMesh Vertex/Index(骨架网格物体 顶点/索引)
- Decal Vertex/Index(Decal 顶点/索引)
- VertexShader(顶点着色器)
- PixelShader(像素着色器)
- Texture Pool Size(贴图池大小)
- FaceFX
由于虚幻引用系统,以上这些很多都与加载游戏内容的大小相关,例如 Animation(动画)、SkeletalMesh(骨架网格物体)、Audio(音频)和 FaceFX。本文将会对这方面的内容进行详细说明。
DVD 空间占用
获得 DVD 上所放置的东西的高层次视图使得我们查看在哪里放置资源来降低空间占用量变得更加容易。是静态网格物体、着色器、贴图或者声效占用了大部分空间吗?
您将需要烘焙您的游戏并运行GAMENAME minecookedpackages命令。它将会使用烘焙的版本的数据更新DB(数据库)。然后由 UBaseDatabaseCommandlet 衍生而来的命令行开关会解压需要的数据来分析磁盘上的内容。例如,AnalyzeDVDSpace 和 FindCinematicTextures。使用基础数据库命令行开关中的框架,添加新的数据挖掘范式是无关紧要的。
可执行文件大小
您的可执行文件尺寸可以耗尽内存。您连接到可执行文件中的代码越多,您的.exe文件会变得越大。我们通常会忘记使用#ifdef来排除掉调试代码。我们也经常会把很多在游戏机平台上不需要的代码连接到可执行文件中。仅是靠查看源代码,找到这些情况中的任何一种都是非常困难的。所以我们需要使用工具来帮助我们完成这个操作。
这里是两个非常酷的工具,它可以帮助我们找出您的.exe文件中的东西。然后一旦您发现了不应该存在于可执行文件的东西,您便可以把它们删除掉! :-)
有规律加载的内容
总是要加载的内容是指Native脚本包中引用的任何东西或者明确地加入到AddToRoot()函数并从来没有移除的东西。基本上,不管游戏类型是什么,对于任何单独的地图来说这些内容都应该在内存中。所以,如果您有一个在整个游戏中仅出现在一张地图中的对象被Native脚本引用,那么每个单独的其它地图都也要加载这个对象。
您应该最小化这种内容的量。如果对于特定的游戏类型您有一些“常用”的对象,那么您可以使用一个CommonGameType对象来引用这些常用对象,当玩那个特定游戏类型时便加载这些对象。
有规律加载的内容页面描述如何查找和处理在不需要的时候有规律加载的内容。
内容成本比较
当您有很多内容时,在游戏中使用的对象通常都会有截然不同的内存消耗。通常这些大量不同具有副作用,即多人创建资源。或者是由于将“Boss Encounter(遭遇 Boss)”变更为只是与迷你 boss 相遇。随着时间发展事件不断发生变化。如果您没有跟踪并与其他人比较资源的方法,那么最终将造成资源使用不当。
从根本上说,我们希望使同样类型的 Weapon(武器)消耗的内存也大致相同。相同类型的角色消耗的内存也大致相同。这样做,可以使我们更容易地策划将要在没有特别包装所有资源的情况下工作的关卡。
GAMENAME ContentComparisonCommandlet
注意: 您可能希望这条指令每夜都可以运行
这会将资源的总内存分为“主要”类别数,然后进行更好的比较。
它们目前位于 BaseEngine.ini,内容如下:
[ContentComparisonReferenceTypes]
+Class=AnimSet(动画集)
+Class=SkeletalMesh(骨架网格物体)
+Class=SoundCue
+Class=StaticMesh(静态网格物体)
+Class=Texture2D(贴图 2D)
由于它们通常代表内存使用的主要情况,并且在资源中最容易进行比较,所以我们现在使用的就是这些属性。
例如,如果您持有 N 武器。您大致可以将它们分成: 小型、中型和大型武器。通常我们不会让小武器的消耗高过大武器。所以可以使每个武器消耗的内存形象化,这个方法非常好,可以确保我们消耗的内存符合规律。
总而言之,我们希望我们的资源类型可以真正做到低标准背离。示例: 如果我们拥有与 N 相似的角色,就 SkeletalMesh(骨架网格物体)而言,我们不希望一个角色消耗的内存比其他所有角色多出 1.5 到 2.0 倍。
一个角色最终会比其他资源消耗更多内存的原因很多。而且其中某些原因只是不知道而已。该命令开关的设计意图是列出消耗,然后确保它们与我们的预期设计相符。这里还会提供一个调查异常值的好方法。
要设置 ContentComparisionCommandlet
浏览的类,请修改 DefaultEditor.ini
ContentComparisonClassesToCompare
内容柱状图
假设您想知道总是需要加载的15兆的声效的情况。但是这些声效是什么哪? 如果您按照列表一个个地听它们,那么您需要听完成百个声效组成的巨大列表,通过这种方式来获得声效的分布状况是非常痛苦的。同样,当您需要加载成百个贴图时也发生了同样的问题。
所以,具有一个可以查看内存中的各种声音类型的高层次的视图便可以为您节约很多时间。如果百分比是正确的那么一切都可以工作的很好。但是,如果您发现您在脚步声效上消耗了您声效预算内存的 40%,那么这里给您提供了快速优化它们的地方。
通过结合使用 MemLeakCheck 和 xls 电子表格,您可以生成有效的柱状图。下面提供了可以用于声音和贴图的样本 .xls 文件: SoundTextureHistograms.xlsx
加载您的关卡,然后进行下面的操作:
- 运行 MemLeakCheck
- 复制并粘帖一系列声效到.xls的SoundData标签下。
- 复制并粘帖一系列贴图到.xls的TextureData 标签下。
- 然后.xls将会给出每个类别及其那个类别所使用的内存量。
注意: 如果您已经添加了 SoundClasses 或 TextureGroups,那么您将更新具有这些类别的 .xls 文件。
引擎内存池
当引擎加载新关卡并创建新对象时,就会有很多随着时间不断增加的对象池/缓冲/列表。它们将会在加载很多关卡之后趋于稳定。问题是它不会“覆盖”所有关卡,也不会常常“清理”所有关卡。所以您可以达到一些正常的“stat memory”类型记录中似乎没有的高水位线。其中有一些是通过调用 WorldInfo.ClearObjectPools 或根据数据池清除函数进行清除。
以下是一些对象池/缓冲/列表的示例:
- 名称表
- Octree
- RB_BodyInstance 对象池 (c.f. DISABLE_POOLED_RB_INSTANCES)
- RB_ConstraintInstance 对象池 (c.f. DISABLE_POOLED_RB_INSTANCES)
- Static Mesh Collision Scales(静态网格物体碰撞缩放)
- 有关渲染主题的 Static Draw(静态绘制)列表
- 使用 EmitterPool 的粒子
- 使用 DecalManager 的贴花
热点报告
热点报告可以用于辨别由于子关卡动态载入和载出而造成的内存减少。它们将允许您的 LD 更清楚地看到消耗最多内存的散列表元,并且应该允许他们优化他们的关卡使其满足内存需求。要获取创建和使用热点报告的完整指南,请参阅热点报告生成页面。
MemLeakCheck Tracking(内存泄露检测追踪)
MemLeakCheck提供了一个关于您的游戏所使用的内存状况的非常简单并容易理解的视图。它仅是一个文本文件,所以您可以您可以通过比较它们的不同来不断地查看它们的变化。
有关这项技术的更多详细信息,请参阅MemLeakCheck 追踪页面。
贴图池
贴图池的大小是固定的,所以当具有大量的贴图时不会对我们整个系统的内存产生任何负面影响。那也就是说我们想确保我们正在高效地使用那个内存。也就意味着我们需要确保那些我们可以看到的贴图所占的内存量在贴图池的大小之内。我们可以在整个地图上使用数G的贴图,只要我们周围的内存区域没有超出贴图池的大小即可。
使用 stat streaming
命令将会显示贴图动态载入统计数据。您所关心的统计数据是:
- 动态载入Fudge Factor(乏析因数): 我们想使得这个值尽可能地接近1.0f。
- Over Budget(超出预算): 我们想使得这个值尽可能地接近0f。
listtextures
控制台命令可以显示当前在池中的所有贴图及其大小。查看那个列表是您可以查看在贴图池中是否存在一些有问题的贴图!
在编辑器中有一个TextureStats(贴图统计数据)标签。在PIE中跑您的关卡,然后查看那个标签,您将会看到很多关于贴图的统计数据。其中一个非常重要的是"Last Seen(sec)(上一次看到贴图(秒))"栏。如果您有一个贴图即使被引用了,但是在那个栏中从来没有看得到它出现过,那么您应该查看哪里使用了它,或许可以删除它!
除了着色器目标之外的所有贴图都使用贴图池。这包括光照贴图。(使用光照贴图而不是顶点光照在内存节约上肯定是个胜利,因为它们将不会使系统内存发生变动。
同时,请确保您的游戏具有理想的贴图池大小。统计数据内存将会显示使用的贴图池大小。基于那些数据,您将可以决定您游戏所要使用的贴图池的最佳大小。
在 DefaultEngine.ini
中:
[TextureStreaming]
PoolSize=120
TArray 闲置
TArrays对于存储数据来说是很好的。问题是在默认的策略中TArray会有很多闲置的元素。另外,即使使用保守的闲置策略,您也会经常遇到那样TArray完成了获取新的数据但是仍然有闲置空间的情况。所以,我们想在任何可能的地方把闲置空间降低为零。
TArray 闲置可以通过启动 TRACK_ARRAY_SLACK
进行跟踪。
内存碎片
MEM STAT: 仅是概要总结。
MEM DETAILED:
这将会输出关于分配器的有用信息。请参阅下面的平台部分。
您需要保留大约一个缓冲的内存(根据平台而定)以便处理碎片问题。您的游戏将会在它达到报告释放内存的 "0" 之前由于内存溢出而崩溃。
栈内存
主线程继承于栈内存。尽管对于您创建的线程,您可以指定您的堆栈大小,如果您使用的是可以在内部创建线程的库,那么它可能不会考虑内存问题。
查找引用的资源
当游戏关卡已经接近完成时,对所有关卡进行一遍加载来确保它们仅加载了需要的资源是值得的。在虚幻引擎3中,任何被引用的内容都会被加载到游戏中,而且引用内容的方法–包括直接地和间接地引用等–这样您最终便会不知不觉地加载了很多的资源。比如,一个Pawn引用一个骨架网格物体,它将会自动地加载那个骨架网格物体所引用的动画集,从而那个动画集的所有动画(Pawn->Skeletalmesh->Animsets->Animations)将会被加载。从内存方面来说,这个链接关系是很危险的,因为游戏有很多Actors之间彼此进行着引用。
一般,您都必须要和关卡设计人员协同工作来决定是否有一些东西不必进行加载或者是否要对动态载入关卡进行分割以及是否不需要同时加载所有关卡。其中的一个实例许是在这个关卡中不需要出现的敌人网格物体。
MEMLEAKCHECK:
Memleakcheck
打印出许多基本游戏数据到一个文本格式的输出文件 [ProfileDirectory:Platform-specific]/MemLeak
中。
这将会至少为您显示骨架网格物体、动画集、声效等物体列表的简单概括信息。一旦您有了基本的信息,您便可以仔细地检查它们来查看是否有不需要加载的资源。如果是这样的,那么可以跟踪查出是谁引用了它,然后删除那个引用是主要的解决方式。
以下是案例学习...
OBJ LIST CLASS=SKELETALMESH:
列出了所有加载的SkeletalMeshes(骨架网格物体),按照占用内存的大小进行排序。MEMLEAKCHECK
包含这个信息,但是它是按照字母顺序排序的,这使得您很难找到要进行优化的内容。
LISTSOUNDS:
列出了所有加载的SoundCues,按照占用内存的大小进行排序。MEMLEAKCHECK
包含这个信息,但是它是按照字母顺序排序的,这使得您很难找到要进行优化的内容。
OBJ LIST CLASS=STATICMESH: 列出了所有加载的静态网格物体,按照占用内存的大小进行排序。
LISTANIMSETS:
列出了所有加载的SoundCues,按照占用内存的大小进行排序。MEMLEAKCHECK 包含这个信息。
OBJ REFS:
用来查找为什么加载某个资源的最好用的方法是使用 OBJ REFS
命令。要想使用这个命令,您需要在PC上运行游戏,因为由于递归函数的存在它需要一个较深的栈,而在UE3的游戏控制台平台版本上通常会崩溃的。
播放到加载有问题的资源的地方,然后输入: OBJ REFS CLASS
NAME= = 。
比如:
OBJ REFS CLASS=SKELETALMESH NAME=BIG_OGRE_2
然后它将会显示引入这个资源的引用物体链,比如:
Log: Shortest reachability from root to SkeletalMesh COG_Bolo_Grenade.Frag_Grenade:
Log: SkeletalMesh COG_Bolo_Grenade.Frag_Grenade [target] (root) (standalone)
Log: SkeletalMeshComponent GearGame.Default__GearProj_FragGrenade:COGGrenadeMesh0 (root) (ObjectProperty Engine.SkeletalMeshComponent:SkeletalMesh)
Log: Class GearGame.GearProj_FragGrenade (root) (standalone)
在上面的实例中,Frag_Grenade网格物体由native 类GearProj_FragGrenade的默认属性所引用。
注意可能会有不止一个引用,在这种情况下您必须一个一个地处理它们。
这里是一些我们找到的没有正确地加载资源的原因:
- 资源被native类引用。
- UnrealScript代码引用这些需要从中获得默认属性的类(该类引用了资源)。
Asset = class'MyGameContent.Pawn_BigOgre'.default.Mesh.PhysicsAsset;
- A Touch Kismet event (SeqEvt_Touch) references the class in its ClassProximityTypes or IgnoredClassProximityTypes arrays. Kismet的Touch事件(SeqEvt_Touch)引用它的ClassProximityTypes或IgnoredClassProximityTypes数组中的类。
- UnrealScript代码引用这些需要从中获得默认属性的类(该类引用了资源)。
游戏控制台平台的内存
- Xbox360: Xenon 内存调试
- PS3: PS3 内存调试