UMA 优化:CPU 可访问纹理和标准重排
通用内存体系结构 (UMA) GPU 与离散 GPU 相比具有一定的效率优势,尤其是在针对移动设备进行优化时。 当 GPU 是 UMA 时,授予资源 CPU 访问权限可以减少 CPU 和 GPU 之间发生的复制量。 虽然我们不建议盲目地让 CPU 访问 UMA 设计上的所有资源,但仍有机会通过提供正确的 CPU 资源访问来提高效率。 与离散 GPU 不同,CPU 在技术上可以指向 GPU 可访问的所有资源。
CPU 可访问纹理的概述
图形管道中的 CPU 可访问纹理是 UMA 体系结构的一项功能,允许 CPU 对纹理进行读写访问。 在更常见的离散 GPU 上,CPU 无法访问图形管道中的纹理。
纹理的一般最佳做法建议是容纳离散 GPU,这通常涉及执行通过缓冲区上传纹理数据中的过程,总结如下:
- 没有对大部分纹理的 CPU 访问权限。
- 将纹理布局设置为D3D12_TEXTURE_LAYOUT_UNKNOWN。
- 使用 CopyTextureRegion 将纹理上传到 GPU。
但是,在某些情况下,CPU 和 GPU 可能会在相同的数据上非常频繁地交互,以便于映射纹理节约电量,或加速有关特定适配器或体系结构的特定设计。 应用程序应检测这些情况,并优化掉不必要的副本。 在这种情况下,为了获得最佳性能,请考虑以下事项:
仅当 D3D12_FEATURE_DATA_ARCHITECTURE::UMA 为 TRUE 时,才开始提高映射纹理的性能。 然后注意 CacheCoherentUMA(如果决定在堆上选择哪些 CPU 缓存属性)。
利用纹理的 CPU 访问权限比利用缓冲区的 CPU 访问权限更复杂。 GPU 最有效的纹理布局很少row_major。 事实上,在复制纹理数据时,某些 GPU 只能支持row_major纹理。
UMA GPU 应通常受益于简单的优化以便减少级别加载时间。 识别 UMA 之后,应用程序可以优化掉初始 CopyTextureRegion 来填充 GPU 不会修改的纹理。 应用程序可以使用 WriteToSubresource 来避免了解实际纹理布局,而不是使用D3D12_HEAP_TYPE_DEFAULT在堆中创建纹理并封送纹理数据。
在 D3D12 中,使用 D3D12_TEXTURE_LAYOUT_UNKNOWN 且没有 CPU 访问权限创建的纹理对于频繁的 GPU 呈现和采样最有效。 在进行性能测试时,应将这些纹理与具有 CPU 访问权限的D3D12_TEXTURE_LAYOUT_UNKNOWN进行比较,将D3D12_TEXTURE_LAYOUT_STANDARD_SWIZZLE与 CPU 访问进行比较,并D3D12_TEXTURE_LAYOUT_ROW_MAJOR跨适配器支持。
将 D3D12_TEXTURE_LAYOUT_UNKNOWN 与 CPU 访问配合使用可启用 WriteToSubresource、 ReadFromSubresource、 Map (阻止应用程序访问指针) 和 Unmap;但可能会牺牲 GPU 访问的效率。
使用具有 CPU 访问权限的D3D12_TEXTURE_LAYOUT_STANDARD_SWIZZLE可启用 WriteToSubresource、 ReadFromSubresource、 Map ((返回指向应用程序) 的有效指针)和 Unmap。 它还可能牺牲 GPU 访问的效率,比 CPU 访问D3D12_TEXTURE_LAYOUT_UNKNOWN更多。
标准重排的概述
D3D12(和 D3D11.3)引入了标准多维数据布局。 这样做是为了对相同数据运行多个处理单元,而不在多个布局之间复制数据或重排数据。 使用标准化的布局,可以通过网络效果提高效率,并允许算法利用快捷方式假设特定模式。
有关纹理布局的详细说明,请参阅 D3D12_TEXTURE_LAYOUT。
请注意,此标准重排是一种硬件功能,并非所有 GPU 都支持该功能。
有关重排的背景信息,请参阅 Z 顺序曲线。
API
与 D3D11.3 不同,D3D12 默认支持纹理映射,因此无需查询 D3D12_FEATURE_DATA_D3D12_OPTIONS。 但是,D3D12 并不总是支持标准重排 - 需要通过调用 CheckFeatureSupport 并检查 standardSwizzle64KBSupported 字段 D3D12_FEATURE_DATA_D3D12_OPTIONS来查询此功能。
以下 API 引用纹理映射:
枚举
- D3D12_TEXTURE_LAYOUT :控制默认纹理的重排模式,并在 CPU 可访问纹理上启用地图支持。
结构
- D3D12_RESOURCE_DESC :描述资源(如纹理),这是一种广泛使用的结构。
- D3D12_HEAP_DESC :描述堆。
方法
- ID3D12Device::CreateCommittedResource:创建单个资源以及具有适当大小和对齐方式的后备堆。
- ID3D12Device::CreateHeap:为缓冲区或纹理创建堆。
- ID3D12Device::CreatePlacedResource:创建放置在特定堆中的资源,这通常是比 CreateHeap 更快的创建资源方法。
- ID3D12Device::CreateReservedResource:创建预留,但尚未提交或放置在堆中的资源。
- ID3D12CommandQueue::UpdateTileMappings:将平铺资源中的平铺位置映射更新到资源堆中的内存位置。
- ID3D12Resource::Map:获取指向资源中的指定数据的指针,并拒绝对子资源的 GPU 访问。
- ID3D12Resource::GetDesc:获取资源属性。
- ID3D12Heap::GetDesc:获取堆属性。
- ReadFromSubresource:从使用 Map 映射的纹理中复制数据。
- WriteToSubresource:将数据复制到使用 Map 映射的纹理中。
资源和父堆具有对齐要求:
- D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT (多样本纹理的 4MB) 。
- D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT (单个样本纹理和缓冲区的 64KB) 。
- 线性子资源复制必须与) D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512 字节对齐,行间距) D3D12_TEXTURE_DATA_PITCH_ALIGNMENT (256 字节。
- 常量缓冲区视图必须与D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT (256 字节) 对齐。
应通过 CreateCommittedResource 处理小于 64 KB 的纹理。
使用动态纹理(更改每个帧的纹理),CPU 将以线性方式写入上传堆,然后执行 GPU 复制操作。
通常,若要创建动态资源,请在上传堆中创建较大的缓冲区(请参阅缓冲区中的子分配)。 若要创建暂存资源,请在读回堆中创建较大的缓冲区。 若要创建默认静态资源,请在默认堆中创建相邻资源。 若要创建默认别名资源,请在默认堆中创建重叠资源。
WriteToSubresource 和 ReadFromSubresource 会在行主序布局和未定义的资源布局之间重新排列纹理数据。 操作是同步的,因此应用程序应记住 CPU 计划。 应用程序始终可以将复制拆分为较小的区域或在另一个任务中计划此操作。 具有不透明资源布局的 MSAA 资源和深度模具资源不受这些 CPU 复制操作支持,并且会导致失败。 没有二次幂元素大小的格式也不受支持,并且也会导致失败。 可能会出现内存不足的返回代码。
相关主题