Windows 图形显示驱动开发-WDDM 2.0-GPU 虚拟地址
图形处理单元 (GPU) 虚拟地址在设备驱动程序接口 (DDI) 级别以逻辑 4KB 或 64KB 页面进行管理。 这使得 GPU 虚拟地址可以引用系统内存(始终以 4KB 粒度分配)或内存段页免(可按 4KB 或 64KB 管理)。
视频内存管理器支持多级虚拟地址转换方案,即使是用多级页表来转换虚拟地址。 级别从 0 开始编号,0 级被分配给叶级别。 转换从根级页表开始。 当页表级数为 2 时,可以调整根页表的大小,以适应 GPU 虚拟地址空间大小可变的进程。 每个级别都由 DXGK_PAGE_TABLE_LEVEL_DESC 结构描述,该结构由内核模式驱动程序在 DxgkDdiQueryAdapterInfo 调用期间填充。 内核模式驱动程序还会填写 DXGK_GPUMMUCAPS 上限结构,以描述 GPU 虚拟寻址支持。
每个进程都有其自己的 GPU 虚拟地址空间。 在设置进程的图形上下文以供执行之前,内核模式驱动程序会收到一个 DxgkDdiSetRootPageTable 调用,该调用会设置根页表地址。
下图显示了两级页表情况下的虚拟地址转换。
GPU 虚拟地址有 DXGK_GPUMMUCAPS::VirtualAddressBitCount 位。
低位 [0 - 11] 表示页面中以字节为单位的偏移量。 接下来的 DXGK_PAGE_TABLE_LEVEL_DESC::PageTableIndexBitCount 位表示叶级别页表中页表项的索引。
页表的条目数为 2^DXGK_PAGE_TABLE_LEVEL_DESC::PageTableIndexBitCount,页表大小为 DXGK_PAGE_TABLE_LEVEL_DESC::PageTableSizeInBytes 字节。
其余位表示根页表中页表项的索引。 对于 2 级转换方案,根页表的大小是可以调整的,并引入了一个新的 DxgkDdiGetRootPageTableSizeDDI 来获取其大小。
DXGK_PTE 结构通过 DDI 表示页表条目。 此结构表示 Microsoft DirectX 图形内核管理的每个条目的相关信息。 驱动程序利用这些信息来生成特定于硬件的页表项。
创建页表分配
页表是以隐式分配方式创建的,没有用户模式驱动程序或内核模式驱动程序句柄。
要分配页表,视频内存管理器将从 DXGK_PAGE_TABLE_LEVEL_DESC::PageTableSegmentId 中指定的段中分配大小为 DXGK_PAGE_TABLE_LEVEL_DESC::PageTableSizeInBytes 的分配。 在创建后,视频内存管理器将使用新的 UpdatePageTable 分页操作将页表中的每个条目初始化为无效。 除了两级转换方案中的根页表外,页表的大小永远不会变化。
在两级转换方案中,视频内存管理器支持调整根页表的大小。 在创建覆盖指定地址空间大小的根页表时,视频内存管理器会调用新的 DxgkDdiGetRootPageTableSizeDDI 来确定所需的分配大小。 然后,视频内存管理器将在段中分配该大小的分配,而该段由 DXGK_PAGE_TABLE_LEVEL_DESC::PageTableSegmentId 为根级别指定。 在创建后,视频内存管理器将使用新的 UpdatePageTable 分页操作将页表中的每个条目初始化为无效。 根页表会随着进程所需视频地址空间的增减而增大或缩小。 在创建根页表后,视频内存管理器将调用新的 DxgkDdiSetRootPageTableDDI 将新创建的根页表与将在其中执行的各种上下文相关联。
在链接显示适配器配置中,根页表创建为 LinkMirrored 分配,其内容完全相同,并位于链接中每个 GPU 的相同物理地址上。 较低级别的页表以 LinkInstanced 分配方式进行分配,以反映其内容在不同 GPU 之间可能会有所不同这一事实,通常是因为不同的对等映射。 页表的内容在所有 GPU 上单独更新。
增大和缩小根页表
本部分仅适用于有两级页表的系统。 当页表级数大于两级时,每一级的页表大小由虚拟寻址上限定义,并且是固定的。
当用户模式驱动程序请求 GPU 虚拟地址时,视频内存管理器会增加进程地址空间的大小以满足请求。 为此,需要增加当前根页表的大小(如有必要),并为新范围分配新的页表。
要扩大根页表,视频内存管理器会创建另一个根页表分配,使其驻留,并使用 UpdatePageTable 操作初始化其条目,然后销毁旧的分配。 DxgkDdiGetRootPageTableSize 函数用于获取新页表的大小(以字节为单位)。
要缩小根页表,视频内存管理器会创建一个新的页表分配,使其常驻,使用 CopyRootPageTable 分页操作将旧页表的一部分复制到新页表,然后再销毁旧分配。
调整大小操作完成后,视频内存管理器会调用 DxgkDdiSetRootPageTableDDI 将受影响的上下文与其新的根页表相关联。
更新页表
当表面在内存中移动时,视频内存管理器会更新页表的内容,以反映表面的新位置。 这是通过新的 UpdatePageTable 分页 DDI 实现的。
移动页表
当设备处于空闲或挂起状态时,视频内存管理器可能会重新定位或驱逐页表。 移动页表时,视频内存管理器会使用 UpdatePageTableDDI 更新上一级页表,以便引用页表的新位置。
当根页表本身被重新定位时,视频内存管理器会调用 DxgkDdiSetRootPageTableDDI,以便通知受影响的上下文其页面目录的新位置。
物理页面大小
如前所述,视频内存管理器支持两种页面大小。 系统内存始终以 4KB 页面为单位进行管理,而内存段则可由内核模式驱动程序决定以 4KB 或 64KB 粒度进行管理。
当选择以 64KB 页面管理虚拟内存时,所有分配都会自动对齐,而大小为 64KB 的倍数。
将所有分配扩展到 64KB 会对内存产生重大影响。 用户模式驱动程序有责任将较小的分配打包到较大的分配中,以避免内存浪费。
将 GPU 虚拟地址映射到一个 64KB 的大型内存段页面时,显存管理器会将 4KB 的页表项映射到内存段中 16 个连续的 4KB 页面。 虚拟地址和物理地址保证共享相同的 64KB 对齐方式(即虚拟地址和物理地址的底部 16 位保证匹配)。