内存管理策略

Direct3D 12 的内存管理器可能会迅速变得非常复杂,原因在于所有对 UMA 或离散(非 UMA)适配器的各层级支持,以及 GPU 适配器之间相当多的体系结构差异。

本部分所述的建议 Direct3D 12 内存管理策略是“分类、预算和流式处理”。

资源类型

“已提交资源”(创建在托管物理内存中初始化的虚拟和物理地址空间)的基本概念从 Direct3D 9 开始就已大致成形,不过,可以在 Direct3D 12 中梳理虚拟寻址 (VA) 和物理寻址,使应用能够精心管理物理内存。

除已提交的资源以外,Direct3D 12 的堆构造还支持其他两种资源类型“定位”和“保留”。 在 Direct3D 11 中,“保留”的资源称为“图块式资源”。

保留的资源与定位的资源的不同之处在于,保留的资源具有自身唯一的 GPU 虚拟地址空间。 这样,便可以提前大规模分配 VA 空间,然后启用 VA 页面到特定堆区段的映射,而应用程序可以即时重新配置安排方式。 VA 空间是连续的,可对其进行稀疏映射。

可将保留的资源设置为使用 UpdateTileMappings 等 API 调用引用堆中的区域,应用可以通过即时更新页面表来将其设置为常驻。 将 VA 范围映射到 NULL 或非常驻堆后,资源的该部分被视为非常驻。 将 VA 范围映射到常驻堆后,资源的该部分被视为常驻。 堆在创建时是常驻的。

定位的资源在设计上要简单得多,它们只是指向特定堆区域的指针(例如,5Mb 堆中为纹理提供的 1Mb 区域)。 别名屏障支持使用重叠放置的资源(请参阅 CreatePlacedResourceResourceBarrier)。

保留的资源并非可在所有 Direct3D 12 硬件上使用,定位的资源是合理的回退,不过,定位的资源必须是连续的,且不能是部分常驻的。

内存预算

在 Direct3D 12 中分配堆时,会创建已提交资源的物理内存属性。 Direct3D 12 中提供了更显式的内存段选项(在视频和系统内存之间选择)。 UMA 适配器只有一个内存段:系统内存。

GPU 不支持页面错误,因此,开发人员必须意识到不能过度提交,尤其是对于系统内存较小(例如只有 1Gb)的系统。 如果应用过度提交,则 OS 会根据其对物理内存的需求,使用粗粒度的进程计划。 计划程序会冻结前台进程并从本质上将其中的某些进程移出页面,以将要运行的后台进程移入页面。 根据用户在后台执行的操作(例如运行浏览器或观看视频),可用的物理内存存在很大的差别。

内存预算 API 为 QueryVideoMemoryInfo。 对于离散的适配器,“local”是视频内存,“non-local”是系统内存。 对于 UMA 适配器,non-local 始终为 0。 一个设计问题是,引擎是要管理这两项预算,还是只管理本地预算。 只管理本地预算会更简单,但需要注意一些问题;例如,假设有一项 1Gb 的最大本地预算,则所有堆将来自 UMA 系统中的该 1Gb 预算,且不会溢出到系统内存(显然,原因是没有系统内存)。

由于 Direct3D11 会为应用程序管理内存,因此会从本质上将未使用的资源移出页面。

选择最适当的资源维度。 考虑资源大小对于实际运行应用程序的场合是否合适。 某些用户可能会在窗口中或使用 800x600 的屏幕分辨率运行应用程序。

分类策略

若要在内存受限的情况下有效管理资源,请考虑将资源分类为:

分类 示例 对象和 API 功能 管理说明
严重 游戏 UI 命令分配器、命令队列、查询堆、资源和资源堆。 这些元素应进入不可分页/始终提交的内存。
缩放/可选 级别特定的模型和纹理、交换链、天空盒、第一人称玩家角色模型 资源和堆。 提交的资源,但定位的资源和保留的资源可能也适用。 将内存驻留预算集成到渲染算法中。 选择适当的可用详细程度,但以低于每帧一次的频率重新评估。 技术包括使用大小可变的资源和交换链缩放。
重复使用的资源 阴影缓冲区、延迟渲染资源、后处理资源、照明数据缓存 资源和堆。 在堆和别名屏障中重叠定位的资源。 在帧中重复使用大型资源或堆区域,以削减整个帧的要求。 使用重用帧内部内存的技术。 在 Direct3D 11 中,应用程序只能重复使用类型相同的且维度可能足够大的资源。 Direct3D 12 堆允许重叠资源,以大幅简化和合理化重复使用。
流式处理资源 地形、开放世界纹理和几何图形 资源和堆。 自由线程创建、后台 CPU 线程,以及后台复制命令队列和列表。 部分驻留,通常基于可见性(使用视图截锥或基于距离的评估),重新评估每个帧的驻留需求。
当 GPU 适配器支持堆中的保留资源时,支持使用按图块的部分驻留管理和帧间重用的技术。
可以实现使用帧间内存重用和部分子资源驻留的技术,但效果欠佳。 包含堆的定位资源应支持更快的回收,但提交的资源可用作回退。

为完成大部分工作流式处理资源而吸引的应用程序越多,它们利用的定位资源和保留资源就越多,因此可在这四种分类之间最大化内存重用。 应用程序流越多,它们的预算和优先化带宽就越高。

通常Direct3D 12 图形引擎需要遵循更多样化的动态预算,这种预算比过去更严格。 最佳的应用程序会将所有四个类别定位到为进程分配的预算,将游戏从后台移动应用扩展为全屏离散预算。 但是,许多应用程序一开始使用了太多的关键资源级别,因此可能会出现困境。 Direct3D 11 允许匿名创建资源,可在不影响性能的情况下占用关键状态。 但是,对于 Direct3D 12,开发人员必须在其整个引擎和中间件中努力搜索随机创建的资源,并将其重新分配到其他类别之一。

其他问题体现在中间件组件、用户控件和帧内部流式处理方面。 中间件组件可能在预算中透露,或者不一定要紧密协作。 中间件组件可能会将功能公开为渲染技术;应用程序可以依赖于公开中间件和引擎设置。 开发人员可以依赖于 Direct3D 11 来执行分页以及获得适当的帧速率。 在某些情况下,Direct3D 11 应用程序可能会不断地将资源内容的页面移入和移出每个帧,使用户可以接受帧速率。 大多数引擎只将资源数据作为后台活动进行流式处理,而不能正常回退到高优先级的帧内部流式处理。 要求引擎实现该功能会抵销改用 Direct3D 12 所带来的一部分 CPU 开销增益。 引擎开发人员可能会考虑分阶段梳理其帧,以便为可重用的资源提供更多机会;他们还可能会与中间件供应商合作,以支持通过定位的资源和堆进行帧内部内存重用。

CreateCommittedResource

CreateReservedResource

Direct3D 12 编程指南

内存管理

资源绑定