unity 移动开发优化 一

一:Crashes:崩溃

Checklist for crashes

  • 关闭 code stripping (and set “slow with exceptions” for iOS)
  • 按照优化ios系统上发布程序的大小,确保不是应为剥离代码引起的崩溃 Optimizing the Size of the Built iOS
    Player (iphone-playerSizeOptimization) to make sure your game does not crash with stripping on iOS.
  • 验证它是不是因为内存不足 (重新启动设备,使用平台最大RAM的设备, be sure to watch the logs)

Editor.log - on the editor

查看unity编辑器下的debug信息,看有没有发生错误

Unity prints some things on the devices as well; Logcat console for Android and Xcode gdb console on iOS devices

Debugging on Android

  1. 使用 DDMS 或者 ADB 工具
  2. 观察stacktrace (Android 3 or newer).使用c++filt (ndk的一部分)或其他方法,如:http://slush.warosu.org/c++filtjs来解码混乱的函数调用
  3. 查看 .so 文件,看看在哪崩溃的:
    1. libunity.so - 崩溃发生在unity代码或者是用户自己写的代码中
    2. libdvm.so - 崩溃发生在java代码中,,查找和java相关的代码(包括修改AndroidManifest.xml).
    3. libmono.so - 要么是 Mono bug 或者是你正在做 Mono 强烈不推荐做的事
  4. If the crashlog does not help you can disassemble it to get a rough understanding of what has happened.
    1. 使用Android NDK 下的 ARM EABI tools from the like this: objdump.exe -S libmono.so >> out.txt
    2. Look at the code around pc from the stacktrace.
    3. try to match that code within the fresh out.txt file.
    4. Scroll up to understand what is happening in the function it occurs in.

Debugging on iOS

  1. Xcode有内置工具. Xcode 4有一个用于调试崩溃的非常好的GUI, Xcode 3少一些.

  2. Full gdb(UNIX及UNIX-like下的调试工具) stack - thread backtrace all

  3. Enable soft-null-check: Enable development build
    and script debugging. Now uncaught null ref exceptions will be printed to the Xcode console with the appropriate managed call stack.

  4. 关闭 “fast script call” 和 code stripping . 它可以阻止一些随机的崩溃,比如使用一些罕见的.net函数或反射造成的崩溃。

Strategy

  1. 尝试找出发生崩溃的脚本,并使用mono在设备上进行调试。
  2. 如果崩溃似乎不在您的代码中,那么仔细看看stacktrace,应该会有一些提示。拿一份交上来,我们会看一下.

二:Profiling

First steps

Unity 依赖 CPU (对其SIMD部分进行了大量优化,比如x86上的SSE或ARM上的NEON)对 皮肤、批处理、物理、用户脚本、粒子等进行处理。

The GPU 用来对 shaders, drawcalls, image effects进行处理.

CPU or GPU bound

  • unity内置的 profiler工具可以检测 CPU 和 GPU ms

Pareto analysis

大多数问题(80%)是由几个关键原因(20%)造成的. 您可以使用Editor profiler来识别处理器最密集的函数调用并首先进行优化. 通常,优化几个关键函数可以显著提高总体执行速度。

您应该确保只在真正需要时才执行函数. 例如,可以使用OnBecameVisible和OnBecameInvisible这样的事件来检测何时看不到对象,并避免更新它. 协同程序可以是一个有用的方式来调用代码,需要定期更新,但不需要运行每一帧:-

// Do some stuff every frame:
void Update () {
}

//Do some stuff every 0.2 seconds:
IEnumerator SlowUpdate () {
   while (true) {
      //do something
      yield return new WaitForSeconds (0.2f);
   }
}


你可以使用.NET System.Threading.Thread类来在单独的线程上运行繁重的计算.这允许你在多核上运行,但注意Unity API不是线程安全的-你需要缓冲输入和结果,并在主线程上读取和分配它们,以便使用Unity API调用。

扫描二维码关注公众号,回复: 9600146 查看本文章

CPU Profiling

Profile user code:分析自己写的代码

并不是所有的代码都显示在 Profiler. 你可以使用  Profiler.BeginSampleProfiler.EndSample 把需要分析的代码显示在profiler中

GPU Profiling

 Unity Editor profiler 目前不能显示 GPU 数据.

Tools for iOS

  • Unity 内置的profiler (not the Editor profiler). 这显示了整个场景的GPU时间
  • PowerVR PVRUniSCo着色器分析仪. See below.
  • iOS
    : Xcode OpenGL ES Driver 仪器只能显示高级信息:
    • “Device Utilization %” - 设备利用率%”- GPU渲染时间的总和。>95%意味着应用程序是GPU绑定的。
    • “Renderer Utilization %” - 渲染器利用率%”- GPU绘制像素的时间
      .
    • “Tiler Utilization %” - Tiler利用率%”- GPU处理顶点的时间
    • “Split count” - “分割计数”-帧分割的数量,其中顶点数据不适合分配的缓冲区。

PowerVR是基于平铺的延迟渲染器,所以不可能获得GPU每帧画面调用的的时间。然而,你可以使用Unity的内置分析器(一个将结果打印到Xcode输出的分析器)获得整个场景的GPU时间。.苹果的工具目前只能告诉你GPU及其部件有多忙,但不会给出以毫秒为单位的时间。

PVRUniSCo 给出整个着色器的周期,以及着色器代码中每一行的近似周期。Windows & Mac! 但无论如何,它都无法与苹果的驱动程序完全匹配。不过,这是一个不错的大致衡量标准。

Tools for Android

  • Adreno (Qualcomm)
  • NVPerfHUD (NVIDIA)
  • PVRTune, PVRUniSCo (PowerVR)

在Tegra上,NVIDIA提供了出色的性能工具,可以做您想做的任何事情- 每一个 draw call所花费的GPU时间, shader循环一次的时间, Force 2x2纹理,空视图矩形,运行在Windows, OSX, Linux。PerfHUD ES不易与消费者设备兼容,你需要NVIDIA

高通提供优秀的Adreno Profiler (Windows only),和消费者设备兼容,它的特点是时间轴图形,帧捕获,帧调试,API调用,着色分析仪,实时编辑。

Graphics related CPU profiling:与图形相关的CPU配置文件

The internal profiler gives a good overview per module:

  • time spent in OpenGL ES API
  • batching efficiency:批处理效率
  • skinning, animations, particles:皮肤,动画,粒子

Profiler Ports

Ports that the Unity profiler uses:

  • MulticastPort : 54998
  • ListenPorts : 55000 - 55511
  • Multicast (unittests) : 55512 - 56023

They should be accessible from within the network node. That is, the devices that you’re trying to profile on should be able to see these ports on the machine with the Unity Editor with the Profiler on.

Memory

有两种类型的内存, Mono memory 和 Unity memory.

Mono memory

Mono memory 处理脚本对象,unity对象的封装 (game objects, assets, components, etc). 当没有足够的可用内存分配时,Garbage Collector会清理掉这部分内存,或者当调用 System.GC.Collect() 方法时

内存以堆块的形式分配。 如果不能将数据放入分配的块中,则可以分配更多的数据. 堆块将保持在Mono,直到应用程序关闭.换句话说,Mono不会释放任何内存给操作系统 (Unity 3.x). 旦你分配了一定数量的内存,它就会被保留给mono,而不是OS. 即使你释放了这部分内存,它也只能在Mono内部使用,而不能用于操作系统。 在 Profiler中堆内存只增不减

如果系统不能将新数据放入分配的堆块中, t Mono 叫做 “GC” and 可以分配一个新的内存空间(for example, due to fragmentation).

“Too many heap sections” 意味着你已经用完了Mono memory (应为fragmentation or heavy usage).

使用 System.GC.GetTotalMemory 以获得总的使用Mono内存。

T一般的建议是,尽量少用一点。

Unity memory

Unity memory处理Asset data (Textures, Meshes, Audio, Animation, etc), Game objects, Engine internals (Rendering, Particles, Physics, etc). Use Profiler.usedHeapSize 获得总的Unity memory.

Memory map

No tools yet but you can use the following.

  • Unity Profiler -不完美,跳过一些东西,但你可以得到一个概述
  • Internal profiler. 显示已使用的堆和已分配的堆。显示每帧分配的mono数量
  • Xcode tools - iOS
  • Xcode Instruments Activity Monitor - 实际内存列.
  • Xcode Instruments Allocations - net allocations for created and living objects.
  • VM Tracker (textures usually get allocated with IOKit label and meshes usually go into VM Allocate).

You can also make your own tool using Unity API calls:-

  • FindObjectsOfTypeAll (type : Type) : Object[]
  • FindObjectsOfType (type : Type): Object[]
  • GetRuntimeMemorySize (o : Object) : int
  • GetMonoHeapSize
  • GetMonoUsedSize
  • Profiler.BeginSample/EndSample - 分析自己的代码
  • UnloadUnusedAssets () : AsyncOperation
  • System.GC.GetTotalMemory/Profiler.usedHeapSize

References to the loaded objects - There is no way to figure this out. A workaround is to “Find references in scene” for public variables.

Garbage collector

  • 当系统无法将新数据放入分配的堆块时,将触发此操作.
  • 不要在移动设备上使用OnGUI():它会在每一帧中射击几次,完全重新绘制视图,并创建大量需要调用垃圾收集的内存分配调用

快速创建/删除大量对象?

  •  这会导致fragmentation.
  • 使用 Editor profiler 追踪查看活动的内存.
  • 内置的l profiler可以查看 mono memory activity.
  • System.GC.Collect()当出现小问题时,可以使用这个. net函数。

Allocation hiccups:分配问题

  • 使用预先分配的列表, 可重用的类实例来实现您自己的内存管理方案。
  • 不要在每一帧进行大量的分配,缓存,预分配

Preallocating a memory pool:预分配内存池。

  • 保留一个不活动的GameObjects的列表,并重用它们,而不是实例化和销毁它们。

Out of mono memory:内存不足

Out of memory crashes:内存不足

在某些情况下,一个游戏可能会因为“内存不足”而崩溃,尽管从理论上讲它应该可以很好地适应.当这种情况发生时,比较一下你的正常游戏内存占用和崩溃时分配的内存大小.如果数字不相似,就会出现内存峰值。这可能是由于:

  • 两个大场景同时加载- 在两个较大的场景之间使用一个空的场景来解决这个问题.
  • 加载了额外的场景 -删除未使用的部分以保持内存大小。
  • 加载了大量的assetbundle包
  • Textures没有适当的压缩(a no go for mobiles).
  • Textures 启用了Get/Set pixel。这需要一个未压缩的纹理拷贝在内存中.
  • Textures 在运行时加载JPEG/PNGs 基本上是没有压缩的
  • 大的mp3文件标记为解压加载.
  • 将未使用的资产保存在mono缓存中,比如静态的monobehavior字段,在更改场景时不会清除这些字段.

三:Optimizations

优化包括以下方面:

    • Resolution:分辨率
    • Post-processing:预处理,相当于滤镜
    • MSAA:抗锯齿
    • Anisotropy:各向异性
    • Shaders
    • Fx/particles density, on/off

Focus on GPUs

图形性能受填充率、像素和几何复杂度(顶点数)的限制. 如果你能找到一种方法来剔除更多渲染器,这三个都可以减少. Occlusion culling可以减少在视角内渲染的图形

在手机上,主要受 fillrate bound 的影响(fillrate = screen pixels * shader complexity * overdraw), 太复杂的shaders是主要原因,所以尽可能的使用简单的shader.

Quality 设置里面降低图片的质量 如果能使游戏运行的更快的话,说明你可能受带宽的影响,所以应该压缩图片,使用mipmaps来降低图片的大小

使用 LOD (Level of Detail) -

Good practice

移动设备的 GPUs 受产生的温度,耗电量以及产生的噪声影响. ,所以比pc有更小的带宽,低ALU性能和变形能力. gpu的架构也被调整为使用尽可能少的带宽和功率。

Unity针对OpenGL ES 2.0进行了优化,它使用GLSL ES(类似于HLSL)着色语言. 内置的着色器通常是用HLSL(也称为Cg)编写的。. 这是交叉编译到GLSL ES的移动平台.如果你愿意,你也可以直接写GLSL,但是这样做会限制你只能写类似opengl的平台(比如移动+ Mac),因为目前还没有GLSL->HLSL翻译工具.当您在HLSL中使用float/half/fixed类型时,它们最终会在GLSL ES中使用highp/mediump/lowp精度限定符。

下是良好实践的清单:

  1. 尽量减少材质material的数量。这使得Unity更容易批量处理东西。
  2. 使用图集atlases (包含一组子图像的大型图像)而不是一些单独的纹理. 加载速度更快,状态开关更少,而且批处理更友好
  3. 使用 Renderer.sharedMaterial而不是 Renderer.material 如果使用纹理地图集和共享material.
  4. Forward rendered pixel lights are expensive.
    • 尽可能使用灯光贴图而不是实时灯光
    • 调整像素灯光的数量在 Quality settings里面. 本质上,只有方向光应该是每个像素,其他一切-每个顶点。当然这取决于游戏。
  5. 尝试在Quality设置灯光的渲染模式,以获得正确的优先级.
  6. 避免裁剪(alpha测试)着色器,除非真的需要.
  7. 保持透明(阿尔法混合)屏幕覆盖到最小.
  8. 尽量避免多重光源照射任何物体的情况
  9. 试着减少着色器通道的总数(阴影,像素光,反射).
  10. 渲染顺序至关重要,一般情况下
    • 完全不透明的物体渲染在前面
    • alpha tested 物体从前往后
    • skybox
    • alpha blended objects (back to front if needed).
  11. Post Processing is expensive on mobiles, use with care.
  12. Particles: 减少drawcall,使用最简单的着色器.
  13. 每帧修改网格的双缓冲:
void Update (){
  // flip between meshes
  bufferMesh = on ? meshA : meshB;
  on = !on;
  bufferMesh.vertices = vertices; // modification to mesh
  meshFilter.sharedMesh = bufferMesh;
}

Shader optimizations

检查你是否被填充率(屏幕的分辨率)限制很简单:如果你降低了显示分辨率,游戏是否运行得更快?如果是,你会受到填充率的限制。

尝试通过以下方法来降低着色器的复杂性:

  •  避免使用alpha-testing shaders; 用 alpha-blended 替代,
  • 使用 simple, optimized shader 代码 (比如unity自带的 “Mobile” shaders .
  • 避免昂贵的 math 方法in shader code (pow, exp, log, cos, sin, tan, etc). 考虑使用预先计算的查找纹理代替
  • 在shader中选择小精度的变量类型 (float, half, fixedin Cg) for best performance.

Focus on CPUs

通常情况下,游戏被GPU限制在像素处理上. 因此,它们最终会有未使用的CPU,特别是在多核移动CPU上.因此,通常明智的做法是把一些工作从GPU转到CPU上(unity可以这么做):比如: mesh skinning, 批处理一些小地对象,更新粒子图形.

这些应该谨慎使用,而不是盲目使用,如果你不顾及draw calls,烘焙其实要更费性能, 因为它使剔除效率更低,使更多的对象受光的影响!

Good practice

  • 不使用 FindObjectsOfType (and Unity getter properties in general) 等方法。
  • 在非移动对象上设置静态属性,以允许内部优化,如静态批处理.
  • 花费大量的CPU周期来进行遮挡剔除和更好的排序(以利用早期的Z-cull)。

Physics

Physics 可以严重影响CPU的性能. 它可以通过 Editor profiler检测到,如果Physics占用太多CPU时间:

  • 调整时间。Time.fixedDeltaTime (in Project settings -> Time),使之尽可能高 ,对于一些刷新不需要那么快的游戏是这样的
  • Physics.solverIterationCount (Physics settings).
  • 使用尽可能少的布质物品Cloth objects
  • 仅在必要时使用刚体 Rigidbodies
  • 优先使用基本的碰撞体 colliders,而不是 mesh colliders.
  • Never ever move a static collider (ie a collider without a Rigidbody) as it causes a big performance hit. It shows up in Profiler as "Static Collider.Move’ but actual processing is in Physics.Simulate. If necessary, add a RigidBody and set isKinematic to true.
  • On Windows you can use NVidia’s AgPerfMon profiling tool set to get more details if needed.

Android

GPU

这些是流行的移动架构. 这既不同于PC/控制台领域的硬件供应商,也不同于“普通”GPU的GPU架构。

  • ImgTec PowerVR SGX - Tile based, deferred: 渲染所有东西在小块(如16x16),阴影只可见像素
  • NVIDIA Tegra - Classic: Render everything
  • Qualcomm Adreno - Tiled: Render everything in tile, engineered in large tiles (as 256k). Adreno 3xx can switch to traditional.
  • ARM Mali Tiled: Render everything in tile, engineered in small tiles (as 16x16)

Spend some time looking into different rendering approaches and design your game accordingly. Pay especial attention to sorting. Define the lowest end supported devices early in the dev cycle. Test on them with the profiler on as you design your game.

Use platform specific texture compression

Further reading

Screen resolution

Android version

iOS

GPU

Only PowerVR architecture (tile based deferred) to be concerned about.

  • ImgTec PowerVR SGX. Tile based, deferred: render everything in tiles, shade only visible pixels.

This means:

  • Mipmaps are not so necessary.
  • Antialiasing and aniso are cheap enough, not needed on iPad 3 in some cases.

And cons:

  • If vertex data per frame (number of vertices * storage required after vertex shader) exceeds the internal buffers allocated by the driver, the scene has to be “split” which costs performance. The driver might allocate a larger buffer after this point, or you might need to reduce your vertex count. This becomes apparent on iPad2 (iOS 4.3) at around 100 thousand vertices with quite complex shaders.
  • TBDR needs more transistors allocated for the tiling and deferred parts, leaving conceptually less transistors for “raw performance”. It’s very hard (i.e. practically impossible) to get GPU timing for a draw call on TBDR, making profiling hard.

Further reading

Screen resolution

iOS version

Dynamic Objects:动态对象

Asset Bundles

  • Asset Bundles 在一定的限制内缓存到设备上
  • 使用Editor API创建Assetbundle
  • 使用 WWW API: WWW.LoadFromCacheOrDownload or as a resource: AssetBundle.CreateFromMemory or AssetBundle.CreateFromFile
  • 卸载使用AssetBundle.Unload. 有一个选项可以卸载捆绑包,但要保留加载的资产。也可以杀死所有加载的资产,即使他们在场景中被引用
  • Resources.UnloadUnusedAssets卸载场景中不再引用的所有资产. 因此,请记住删除对不需要的资产的引用. Public和static变量永远不会被垃圾收集 never garbage collected.
  • Resources.UnloadAsset 从内存中卸载特定的资产。如果需要,可以从磁盘重新加载它。

在iOS上同时下载Assetbundle的次数有限制吗?(e。g我们能否同时下载超过10个资产包(或每一帧)? )

下载是通过OS提供的异步API实现的,所以OS决定需要为下载创建多少线程. 当启动多个并发下载时,您应该记住它可以支持的设备总带宽和可用内存的数量. 每个并发下载都分配自己的临时缓冲区,因此您应该小心不要耗尽内存。

Resources

  • Assets need to be recognized by Unity to be placed in a build.
  • 将.bytes文件扩展名添加到您希望Unity识别为二进制数据的任何原始字节中.
  • 添加.txt文件扩展到任何文本文件,你想要Unity识别为一个文本资产
  • 资源在构建时转换为平台格式。
  • Resources.Load()

Issue checklist:问题清单

  • Textures 没有适当的压缩
  • 不同的解决方案适用于不同的情况,但是一定要压缩纹理,除非你确定你不应该这样做
  • ETC/RGBA16 -默认为android,但可以调整取决于GPU供应商. 在需要使用的地方使用ETC, Alpha textures可以使用两个ETC文件,其中一个文件使alpha通道
  • PVRTC- default for iOS, good for most cases
  • Textures 关闭 Get/Set pixels enabled - 如果不是特别需要,取消勾选 Get/Set is needed
  • 运行时从JPEG/ png加载的纹理将被解压
  • 大的mp3文件标记为解压加载
  • 加载了额外的场景
  • 未在内存中清理的未使用资产
  • 如果它随机崩溃,试试devkit或2 GB内存的设备(如Ipad 3)。

Sometimes there’s nothing in the console, just a random crash

  • Fast script call and stripping may lead to random crashes on iOS. Try without them.
发布了80 篇原创文章 · 获赞 7 · 访问量 2678

猜你喜欢

转载自blog.csdn.net/qq_37672438/article/details/104472463