居住

当 GPU 可访问对象时,该对象被视为 驻留

驻留预算

GPU 尚不支持页面故障,因此应用程序必须在 GPU 可以访问数据时将数据提交到物理内存中。 此过程称为“使内容成为居民”,必须为物理系统内存和物理离散视频内存完成。 在 D3D12 中,大多数 API 对象封装了一些 GPU 可访问的内存。 该 GPU 可访问的内存在创建 API 对象期间驻留,并在 API 对象销毁时被逐出。

进程可用的物理内存量称为视频内存预算。 随着后台进程唤醒和睡眠,预算可能会明显波动:当用户切换到另一个应用程序时,会大幅波动。 当预算更改并轮询当前预算和当前消耗的内存量时,可以通知应用程序。 如果应用程序未保持在其预算范围内,该过程将间歇性冻结,以允许其他应用程序运行和/或创建 API 将返回失败。 IDXGIAdapter3 接口提供了与此功能相关的方法,特别是 QueryVideoMemoryInfoRegisterVideoMemoryBudgetChangeNotificationEvent

鼓励应用程序使用预留来表示它们不能不使用的内存量。 理想情况下,用户指定的“低”图形设置(甚至更低)是此类预留的正确值。 设置预留永远不会为应用程序提供比通常收到的更高的预算。 相反,预留信息可帮助 OS 内核快速最大程度地减少大型内存压力情况的影响。 即使预留不保证在应用程序不是前台应用程序时可供应用程序使用。

堆资源

虽然许多 API 对象封装了一些 GPU 可访问的内存,但堆 & 资源应该是应用程序使用和管理物理内存的最重要方式。 堆是管理物理内存的最低级别单位,因此最好熟悉其驻留属性。

  • 堆不能部分驻留,但保留资源存在解决方法。
  • 堆应作为特定池的一部分进行预算。 UMA 适配器有一个池,而离散适配器有两个池。 虽然内核可以将离散适配器上的一些堆从视频内存转移到系统内存,但它只是作为极端的最后手段。 应用程序不应依赖于内核的预算过度行为,而是应专注于良好的预算管理。
  • 堆可以从驻留中逐出,从而允许将其内容分页到磁盘。 但是,堆的销毁是一种更可靠的技术,用于释放所有适配器体系结构的驻留。 在 D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT 的MaxGPUVirtualAddressBitsPerProcess 字段接近预算大小的适配器上,逐出 无法可靠地回收驻留。
  • 堆创建速度可能很慢;但它已针对后台线程处理进行优化。 建议在后台线程上创建堆,以避免呈现线程出现故障。 在 D3D12 中,多个线程可以安全地调用创建例程。

D3D12 为其资源模型引入了更大的灵活性和正交性,以便为应用程序提供更多选项。 D3D12 中有三种高级类型的资源:提交、放置和保留。

  • 提交的资源同时创建一个资源和一个堆。 堆是隐式的,不能直接访问。 堆的大小适当,用于在堆中查找整个资源。
  • 放置的资源允许将资源放置在堆中的非零偏移量处。 偏移量通常必须与 64KB 对齐;但两个方向都存在一些异常。 MSAA 资源需要 4MB 偏移对齐方式,4KB 偏移对齐可用于小型纹理。 放置的资源不能直接重新定位或重新映射到另一个堆;但是,它们支持在堆之间简单重定位资源数据。 在不同的堆中创建新放置的资源并复制资源数据后,必须将新的资源描述符用于新的资源数据位置。
  • 仅当适配器支持平铺资源第 1 层或更高版本时,保留资源才可用。 当可用时,它们提供可用的最先进的驻留管理技术;但并非所有适配器当前都支持它们。 它们支持重新映射资源,而无需重新生成资源描述符、部分 mip 级别驻留和稀疏纹理方案等。并非所有资源类型都受支持,即使保留资源可用,因此完全基于页面的驻留管理器尚不可行。

驻留优先级

Windows 10 创意者更新使开发人员能够影响当内存压力要求降级某些资源时,哪些堆和资源更倾向于保持驻留状态。 这有助于开发人员利用运行时无法从 API 使用情况推断的知识来创建更好的应用程序。 预计开发人员在从使用提交的资源过渡到重新部署和平铺资源时,将更加舒适且能够指定优先级。

应用这些优先级必须比管理两个动态内存预算(手动降级和提升资源)更简单,因为应用程序已经可以做到这一点。 因此,驻留优先级 API 的设计当然具有合理的默认优先级,这些优先级将分配给每个堆或资源(在其创建时)。 有关详细信息,请参阅 ID3D12Device1::SetResidencyPriorityD3D12_RESIDENCY_PRIORITY 枚举。

优先顺序为开发人员:

  • 提高一些异常堆的优先级,以更好地缓解这些堆的体验性能影响,这些堆的降级频率高于其自然访问模式的需求。 此方法预计将由从图形 API(如 Direct3D 11 或 OpenGL)移植的应用程序利用,其资源管理模型明显不同于 Direct3D 12 模型。
  • 根据程序员对访问频率或动态的认识,使用应用程序自己的存储桶化方案替代几乎所有堆优先级,要么是固定的;固定方案比动态方案更易于管理,但效率较低,并且要求程序员在开发过程中使用模式发生变化。 此方法预计将由使用 Direct3D 12 样式资源管理构建的应用程序(例如使用驻留库(尤其是动态方案)使用的应用程序利用。

默认优先级算法

应用程序无法为它尝试管理的任何堆指定有用的优先级,而无需先实例化默认优先级算法。 这是因为将特定优先级分配给堆的值派生自其相对优先级,派生到争用同一内存的其他优先堆。

选择用于生成默认优先级的策略是将堆分类为两个存储桶,优先于(优先于)堆,这些堆假定由 GPU 频繁写入,而堆不是。

高优先级存储桶包含使用标志创建的堆和资源,这些标志将其标识为呈现目标、深度模具缓冲区或无序访问视图(UAV)。 这些值是在从 D3D12_RESIDENCY_PRIORITY_HIGH开始的范围中分配的优先级值;若要在这些堆和资源中进一步确定优先级,优先级最低的 16 位设置为堆或资源的大小除以 10MB(饱和为极大型堆的0xFFFF)。 这种额外的优先顺序有利于更大的堆和资源。

低优先级存储桶包含所有其他堆和资源,这些堆和资源分配 D3D12_RESIDENCY_PRIORITY_NORMAL的优先级值。 未尝试在这些堆和资源之间进行进一步的优先顺序。

编程驻留管理

简单的应用程序可能只需创建已提交的资源,直到遇到内存不足故障为止。 失败后,应用程序可以销毁其他已提交的资源或 API 对象,以使进一步的资源创建成功。 但是,强烈建议使用简单的应用程序来监视负面预算更改,并大致销毁一个帧中未使用的 API 对象。

尝试优化适配器体系结构或合并驻留优先级时,驻留管理设计的复杂性将会上升。 离散预算和管理两个离散内存池比仅管理一个池要复杂得多,如果使用模式发展,则分配固定优先级可能会成为维护负担。 将纹理溢出到系统内存会增加复杂性,因为系统内存中的错误资源可能会严重影响帧速率。 并且,没有简单的功能来帮助识别资源,这些资源既会受益于更高的 GPU 带宽,要么容忍较低的 GPU 带宽。

更复杂的设计将查询当前适配器的功能。 此信息在 D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORTD3D12_FEATURE_DATA_ARCHITECTURED3D12_TILED_RESOURCES_TIERD3D12_RESOURCE_HEAP_TIER中提供。

应用程序的多个部分可能最终使用不同的技术。 例如,某些大型纹理和很少练习的代码路径可能使用已提交的资源,而许多纹理可以使用流属性指定,并使用常规放置资源技术。

ID3D12Heap

内存管理