Version: 2020.1
CullingGroup API
优化图形性能

加载纹理和网格数据

Unity 可从磁盘加载纹理和网格数据,并以两种不同方式将数据上传到 GPU:同步或异步。这两个过程称为同步上传管线和异步上传管线。

当 Unity 使用同步上传管线时,在加载和上传数据时无法执行其他任务。这种情况下可能会导致应用程序出现明显的暂停。当 Unity 使用异步上传管线时,数据在后台加载和上传,同时可以执行其他任务。

如果某个纹理或网格适合异步上传管线,则 Unity 会自动使用异步上传管线。如果某个纹理或网格不适合异步上传管线,则 Unity 会自动使用同步上传管线。

工作原理

同步和异步上传管线之间的主要区别是 Unity 在构建时保存数据的位置,这会影响 Unity 在运行时加载数据的方式。

在同步上传管线中,Unity 必须在单个帧中同时加载纹理或网格的元数据(标头数据)和纹素或顶点数据(二进制数据)。在异步上传管线中,Unity 必须在单个帧中仅加载标头数据,并可以在后续帧中将二进制数据流式传输到 GPU。

在同步上传管线中:

  • 在构建时,Unity 将网格或纹理的标头数据和二进制数据都写入同一 .res 文件。
  • 在运行时,当应用程序需要纹理或网格的时候,Unity 将该纹理或网格的标头数据和二进制数据从 .res 文件加载到内存中。当所有数据都位于内存中时,Unity 随后将二进制数据从内存上传到 GPU。加载和上传操作主要发生在主线程上的单个帧中。

在异步上传管线中:

  • 在构建时,Unity 将标头数据写入到一个 .res 文件,而将二进制数据写入到另一个单独的 .resS 文件。
  • 在运行时,当应用程序需要纹理或网格的时候,Unity 将标头数据从 .res 文件加载到内存中。当标头数据位于内存中时,Unity 随后使用固定大小的环形缓冲区将二进制数据从 .resS 文件流式传输到 GPU。Unity 使用多个线程通过几个帧流式传输二进制数据。请注意,在 Unity 已了解 GPU 硬件的某些游戏主机平台上,Unity 会跳过环形缓冲区并直接加载到 GPU 内存中。

纹理和网格数据适合的上传管线

如果满足以下条件,纹理适合异步上传管线:

  • 纹理未启用读/写权限。
  • 纹理不在 Resources 文件夹中。
  • 如果构建目标是 Android,在项目的构建设置 (Build Settings) 中启用了 LZ4 压缩。

请注意,如果使用 LoadImage(byte[] data) 来加载纹理,即使满足上述条件,仍会强制 Unity 使用同步上传管线。

如果满足以下条件,网格适合异步上传管线:

  • 网格未启用读/写权限。
  • 网格不在 Resources 文件夹中。
  • 网格没有 BlendShape
  • Unity 尚未将动态批处理 (Dynamic Batching) 应用于网格,原因是网格不适合进行动态批处理,或者是因为禁用了动态批处理。有关动态批处理的更多信息,请参阅绘制调用批处理
  • 粒子系统、地形或网格碰撞体不需要网格顶点/索引数据。
  • 网格没有骨骼权重
  • 网格拓扑不是四边形
  • 网格资源的 meshCompression 设置为 Off。 如果构建目标是 Android,在项目的 Build Settings 中启用了 LZ4 压缩。

在所有其他情况下,Unity 会同步加载纹理和网格。

如何识别 Unity 所使用的上传管线

可使用性能分析器或其他性能分析工具通过观察线程活动和性能分析器标记来确定 Unity 在何时使用了异步上传管线。

以下内容表明 Unity 正在使用异步上传管线来上传纹理或网格:

  • AsyncUploadManager.ScheduleAsyncReadAsyncReadManager.ReadFileAsync.DirectTextureLoadBegin 性能分析器标记。
  • AsyncRead 线程上的活动。

如果没有看到此活动,则表明 Unity 没有使用异步上传管线。

请注意,以下性能分析器标记并不表示 Unity 正在使用异步上传管线;Unity 调用它们来检查是否需要进行任何异步上传工作:

  • Initialization.AsyncUploadTimeSlicedUpdate
  • AsyncUploadManager.AsyncResourceUpload
  • AsyncUploadManager.ScheduleAsyncCommands

配置异步上传管线

可配置异步上传管线的以下设置。请注意,无法配置同步上传管线的设置。

Quality 设置中的异步上传设置
Quality 设置中的异步上传设置

Async Upload Buffer

Unity 重复使用单个环形缓冲区将纹理和网格数据流式传输到 GPU。如此将减少所需的内存分配次数。

Async Upload Buffer 决定了此环形缓冲区的大小(以 MB 为单位)。该值的最小大小为 2,最大大小为 512。

Unity 会自动调整缓冲区的大小以适应当前加载的最大纹理或网格。这可能是一个缓慢的操作,尤其是在 Unity 必须多次执行该操作的情况下;例如,如果要加载许多大于默认缓冲区大小的纹理,则属于这种情况。为了减少 Unity 必须调整缓冲区大小的次数,请设置该值以适合您期望加载的最大值。通常这是场景中最大的纹理。

可在 Quality settings 窗口中或使用 QualitySettings.asyncUploadBufferSize API 来配置该值。

Async Upload Time Slice

Async Upload Time Slice 是 CPU 将纹理或网格数据上传到 GPU 时花费的时间量(以毫秒/帧为单位)。

较大的值表示数据将更快地在 GPU 上准备就绪,但是 CPU 将在这些帧期间花费更多时间进行上传操作。请注意,只有在缓冲区中有数据等待上传到 GPU 时,Unity 才使用此时间进行上传;如果没有等待的数据,Unity 可以将此时间用于其他操作。

可在 Quality settings 窗口中或使用 QualitySettings.asyncUploadTimeSlice API 来配置该值。

其他信息

有关异步上传纹理和网格数据的更多信息,请参阅 Unity 博客文章:优化加载性能:了解异步上传管线 (Optimizing loading performance: Understanding the Async Upload Pipeline)

CullingGroup API
优化图形性能