DXGKDDI_BUILDPAGINGBUFFER回调函数 (d3dkmddi.h)

DxgkDdiBuildPagingBuffer 函数为内存操作生成分页缓冲区。

语法

DXGKDDI_BUILDPAGINGBUFFER DxgkddiBuildpagingbuffer;

NTSTATUS DxgkddiBuildpagingbuffer(
  [in]     IN_CONST_HANDLE hAdapter,
  [in/out] IN_PDXGKARG_BUILDPAGINGBUFFER pBuildPagingBuffer
)
{...}

参数

[in] hAdapter

与显示适配器关联的上下文块的句柄。 显示微型端口驱动程序之前在 DxgkDdiAddDevice 函数的 MiniportDeviceContext 输出参数中向 Microsoft DirectX 图形内核子系统提供了此句柄。

[in/out] pBuildPagingBuffer

指向 DXGKARG_BUILDPAGINGBUFFER 结构的指针,该结构包含用于生成分页缓冲区的信息。

返回值

DxgkDdiBuildPagingBuffer 返回以下值之一:

返回代码 说明
STATUS_SUCCESS DxgkDdiBuildPagingBuffersuccessfully 生成分页缓冲区。
STATUS_GRAPHICS_ALLOCATION_BUSY GPU 当前使用分页缓冲区的分配。
STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER 分页缓冲区 (即 pBuildPagingBuffer 参数指向 DXGKARG_BUILDPAGINGBUFFER 结构的 pDmaBuffer 成员中需要更多的空间) 。

注解

调用 DxgkDdiBuildPagingBuffer 函数以生成特殊用途的直接内存访问 (DMA) 缓冲区(称为 分页缓冲区)。 分页缓冲区包含一个操作,该操作可移动部分分配的内容:

  • 在分配的段内。
  • 在分配段之间。
  • 从分配段到系统内存。
  • 从系统内存到分配段。

显示微型端口驱动程序必须根据请求的分页操作,在DXGKARG_BUILDPAGINGBUFFER) 的 pDmaBuffer 成员中提供的分页缓冲区 ( (GPU) 指令 编写相应的图形处理单元;然后,驱动程序必须将分页缓冲区返回到视频内存管理器 (,该管理器是 Dxgkrnl.sys) 的一部分。 GPU 计划程序 (它也是 Dxgkrnl.sys) 随后调用驱动程序的 DxgkDdiSubmitCommand 函数,请求驱动程序将分页缓冲区作为常规 DMA 缓冲区提交到 GPU。

注意 在视频内存管理器提交分页缓冲区之前,它会调用驱动程序的 DxgkDdiPatch 函数来分配 (即将) 物理地址 修补 到分页缓冲区;但是,在调用 DxgkDdiPatch 时,视频内存管理器不提供修补程序位置列表。 驱动程序的 DxgkDdiPatch 函数可以对分页缓冲区执行最后一分钟更新;但是,驱动程序的 DxgkDdiPatch 函数无法更改分页缓冲区的大小。
 
当驱动程序成功生成分页缓冲区时,驱动程序的 DxgkDdiBuildPagingBuffer 应更新 pDmaBuffer ,使其指向写入分页缓冲区的最后一个字节,然后返回STATUS_SUCCESS。 由于 DxgkDdiBuildPagingBuffer 只有在分页缓冲区空间不足时才会失败,因此驱动程序应始终验证分页缓冲区在写入缓冲区之前是否有足够的剩余空间。 如果分页缓冲区中没有足够的空间,驱动程序应返回STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER。 然后,视频内存管理器将获取新的分页缓冲区,并再次调用驱动程序的 DxgkDdiBuildPagingBuffer 函数,以根据请求的分页操作填充新的分页缓冲区。 请注意,对于填充多个分页缓冲区的给定请求分页操作,计划程序针对每个分页缓冲区多次调用驱动程序的 DxgkDdiSubmitCommand 函数,以独立提交每个缓冲区。

如果 DxgkDdiBuildPagingBuffer 确定分页操作需要多个分页缓冲区,则驱动程序可以在 DXGKARG_BUILDPAGINGBUFFERMultipassOffset 成员中指定信息,并且可以跨分页操作的多次迭代使用此信息。 视频内存管理器在第一个分页操作请求之前将 MultipassOffset 中的信息初始化为零,并且不会在迭代之间修改 MultipassOffset 中的信息。 因此,驱动程序可以使用 MultipassOffset 保存迭代之间的进度。 例如,驱动程序可以存储上次为基于分页的传输传输的页码。

当前为以下类型的操作生成分页缓冲区:

  • 传输

    传输操作将分配的内容从一个位置移到另一个位置。 此操作是最常见的内存操作类型。

    分配始终完全从一个位置转移到另一个位置。 但是,由于内存约束,分配的传输可以分为多个子传输 (即,分配的一部分从位置 A 移动到 B,然后移动以下部分,依此类移,直到整个分配) 传输。 分配的第一个子转移在 DXGKARG_BUILDPAGINGBUFFER 的 Transfer 成员的 Flags 成员中标有 TransferStart 位字段标志;分配的最后一次子传输使用 TransferEnd 位字段标志进行标记。 保证驱动程序收到挂起的转移 (即驱动程序接收新传输开始之前的最后一个子传输) 结束。

    每个子传输可能需要多次调用 DxgkDdiBuildPagingBuffer 才能完成 (例如,驱动程序可能会用完 DMA 缓冲区空间) 。 因此,驱动程序可能会在多次调用 DxgkDdiBuildPagingBuffer 时收到 TransferStart 标志,直到驱动程序在调用 DxgkDdiBuildPagingBuffer 时收到 TransferEnd 标志。 多次接收 TransferStart 标志并不表示多个新传输的开始;它指示分配的子传输需要多次迭代 (例如,如果驱动程序用完 DMA 缓冲区空间) 。 驱动程序可以使用 DXGKARG_BUILDPAGINGBUFFERMultipassOffset 成员跟踪 DxgkDdiBuildPagingBuffer 的多个迭代中特定子传输的进度。

    通常,传输发生在单个操作中。 在这种情况下,将设置 TransferStartTransferEnd 位字段标志。

    在某些情况下,当某些分配被分页到内存中或内存外时,可能需要驱动程序来设置硬件资源。 默认情况下,GPU 可能使用调用 DxgkDdiBuildPagingBuffer 期间引用的分配。 在这些方案中,驱动程序可能需要在驱动程序对所需硬件资源进行编程之前将分配处于空闲状态 (即,无法在提供的 DMA 缓冲区) 中对硬件资源进行编程。 对于此类方案,驱动程序可能会使对 DxgkDdiBuildPagingBuffer 的调用失败,STATUS_GRAPHICS_ALLOCATION_BUSY。

    如果驱动程序返回STATUS_GRAPHICS_ALLOCATION_BUSY,则视频内存管理器将等待 GPU 完成对当前分配的任何引用,然后再次调用驱动程序的 DxgkDdiBuildPagingBuffer 函数。 在对 DxgkDdiBuildPagingBuffer 的第二次调用中,视频内存管理器在 DXGKARG_BUILDPAGINGBUFFER 的 Transfer 成员的 Flags 成员中设置 AllocationIsIdle 位字段标志,以指示所引用的分配处于空闲状态。 如果未设置空闲标志,驱动程序应始终确定分配当前正忙,或者可能很快就会变得繁忙。 如果设置了空闲标志,则视频内存管理器将保证所引用的分配在调用 DxgkDdiBuildPagingBuffer 期间保持空闲状态。

    如果 DXGKARG_BUILDPAGINGBUFFER 的 hAllocation 成员为 NULL,则驱动程序应将源中的数据复制到目标,而不执行任何重排或平铺。

  • Fill

    填充操作使用指定的模式填充分配。 填充操作用于设置分配的初始内容。 填充分配内容时,保证分配 (处于空闲状态,而不是 GPU) 使用。 填充操作只能在内存段上执行。 视频内存管理器从不请求显示微型端口驱动程序填充光圈段。

  • 放弃内容

    放弃内容操作通知驱动程序分配已从内存段的当前位置放弃分配。 也就是说,分配被逐出,并且不会复制回系统内存。

    在某些情况下,当某些分配被分页到内存中或内存外时,可能需要驱动程序来设置硬件资源。 默认情况下,GPU 可能会使用调用 DxgkDdiBuildPagingBuffer 期间引用的分配。 在这些方案中,驱动程序可能需要在驱动程序对所需硬件资源进行编程之前将分配处于空闲状态 (即,无法在提供的 DMA 缓冲区) 中对硬件资源进行编程。 对于此类方案,驱动程序可能会使对 DxgkDdiBuildPagingBuffer 的调用失败,STATUS_GRAPHICS_ALLOCATION_BUSY。

    如果驱动程序返回STATUS_GRAPHICS_ALLOCATION_BUSY,则视频内存管理器将等待 GPU 完成对当前分配的任何引用,然后再次调用驱动程序的 DxgkDdiBuildPagingBuffer 函数。 在对 DxgkDdiBuildPagingBuffer 的第二次调用中,视频内存管理器在 DXGKARG_BUILDPAGINGBUFFER 结构的 DiscardContent 成员的 Flags 成员中设置 AllocationIsIdle 位字段标志,以指示所引用的分配处于空闲状态。 如果未设置空闲标志,驱动程序应始终确定分配当前正忙,或者可能很快就会变得繁忙。 如果设置了空闲标志,则视频内存管理器将保证所引用的分配在调用 DxgkDdiBuildPagingBuffer 期间保持空闲状态。

  • 读取物理

    读取物理操作从指定的物理内存地址读取。 请求驱动程序为操作对 GPU 进行编程。 要访问读取的物理内存大小可以是 1 字节到 8 个字节。 由于读取的数据不相关, 因此 DxgkDdiBuildPagingBuffer 不需要返回数据。 但是,如果 CPU 在 GPU 写入该 AGP 内存后尝试从 AGP 内存中读取数据,则读取物理操作对于确保内存一致性至关重要。

  • 写入物理

    写入物理操作写入指定的物理地址。 请求驱动程序为操作对 GPU 进行编程。 写入操作要访问的物理内存大小可以是 1 字节到 8 个字节。 由于写入的数据无关紧要, DxgkDdiBuildPagingBuffer 可以将任何数据写入内存。 但是,如果 CPU 在 GPU 写入该 AGP 内存后尝试从 AGP 内存中读取数据,则写入物理操作对于确保内存一致性至关重要。

  • 地图光圈段

    map-aperture-segment 操作将指定的内存描述符列表 (MDL) 映射到指定页数的指定段偏移量处的指定光圈段。 如果在 DXGKARG_BUILDPAGINGBUFFER 结构的 MapApertureSegment 成员的 Flags 成员中设置了 CacheCoherent 位字段标志,则驱动程序必须确保在映射的页面上强制实施缓存一致性;否则,映射的页面不需要缓存一致性。

    注意 仅当可缓存内存映射到缓存一致光圈段时,才设置 CacheCoherent 位字段标志,并且永远不会在非缓存一致光圈段或映射到缓存一致段的写入组合分配上设置。
     
    驱动程序可以选择使用内存映射 I/O (MMIO) 来配置光圈段。 GPU 在配置时不会访问光圈范围。 但是,此光圈配置不得干扰 GPU 的执行。 在设置了 DXGK_OPERATION_MAP_APERTURE_SEGMENT 操作类型的情况下调用 DxgkDdiBuildPagingBuffer 时,GPU 不会处于空闲状态,并且 GPU 可能正忙于访问正在重新配置的光圈段的其他部分。
  • 取消映射光圈段

    unmap-aperture-segment 操作取消映射指定光圈段先前映射的范围。 驱动程序必须将未映射的区域映射到 DXGKARG_BUILDPAGINGBUFFER 结构的UnmapApertureSegment 成员的 DummyPage 成员指定的虚拟页面。

    注意 当驱动程序取消映射到虚拟页面时,驱动程序必须通过指定的光圈范围启用 GPU 访问,以便 DirectX 图形内核子系统可以检测损坏问题。 存在一致性测试来检查这种情况。
     
    视频内存管理器使用光圈未映射部分中的虚拟页面来确定内存管理器访问光圈段的困难。

    驱动程序可以选择使用 MMIO 来配置光圈段。 GPU 在配置时不会访问光圈范围。 但是,此光圈配置不得干扰 GPU 的执行。 如果调用 DxgkDdiBuildPagingBuffer 并设置了DXGK_OPERATION_UNMAP_APERTURE_SEGMENT操作类型,并且 GPU 可能正忙于访问正在重新配置的光圈段的其他部分,则 GPU 将不会处于空闲状态。

  • 特殊锁传输

    特殊锁定-传输操作类似于常规传输操作。 但是,在调用 pfnLockCb 函数并设置 UseAlternateVA 位字段标志时,special-lock-transfer 操作不会将分配内容从 或 转移到分配的常规后备存储中,而是将分配内容从 或 传输到为分配设置的备用虚拟地址。

    特殊锁传输操作仅在以下方案中发生:

    • 分配当前可通过备用虚拟地址访问 CPU,并且正在逐出。
    • 以前被逐出的分配(如上一项目符号中所述的情况)将重新分页。
    不支持使用 UseAlternateVA 位字段标志的驱动程序不会被调用来执行特殊锁定传输操作。

    在某些情况下,当某些分配被分页到内存中或内存外时,可能需要驱动程序来设置硬件资源。 默认情况下,GPU 可能使用调用 DxgkDdiBuildPagingBuffer 期间引用的分配。 在这些方案中,驱动程序可能需要在驱动程序对所需硬件资源进行编程之前将分配处于空闲状态 (即,无法在提供的 DMA 缓冲区) 中对硬件资源进行编程。 对于此类方案,驱动程序可能会使对 DxgkDdiBuildPagingBuffer 的调用失败,STATUS_GRAPHICS_ALLOCATION_BUSY。

    如果驱动程序返回STATUS_GRAPHICS_ALLOCATION_BUSY,则视频内存管理器将等待 GPU 完成对当前分配的任何引用,然后再次调用驱动程序的 DxgkDdiBuildPagingBuffer 函数。 在对 DxgkDdiBuildPagingBuffer 的第二次调用中,视频内存管理器在 DXGKARG_BUILDPAGINGBUFFER 结构的 SpecialLockTransfer 成员的 Flags 成员中设置 AllocationIsIdle 位字段标志,以指示所引用的分配处于空闲状态。 如果未设置空闲标志,驱动程序应始终确定分配当前正忙,或者可能很快就会变得繁忙。 如果设置了空闲标志,则视频内存管理器将保证所引用的分配在调用 DxgkDdiBuildPagingBuffer 期间保持空闲状态。

请注意,如果驱动程序必须使用硬件光圈来线性化应用程序可以直接访问的重排分配,驱动程序必须取消重排该分配,而驱动程序会将分配传输到系统内存,以保持分配虚拟地址的一致性。 驱动程序必须取消重排分配,因为应用程序访问分配时可能发生逐出。

系统的内存管理器可确保传输对应用程序不可见。 但是,由于分配位于系统内存中,并且分配的虚拟地址无法再通过硬件光圈,因此驱动程序必须确保系统内存中的字节顺序与通过光圈可见的内容匹配。

应使 DxgkDdiBuildPagingBuffer 可分页。

示例

下面的代码示例演示如何使用 DxgkDdiBuildPagingBuffer

NTSTATUS ntStatus;
DXGKARG_BUILDPAGINGBUFFER param;

// The driver receives the following paging operation to build:
//
param.Flags = 0;
param.pDmaBuffer= CurrentPagingBuffer;
param.DmaSize = CurrentPagingBufferSizeLeft;
param.pDmaBufferPrivateData = CurrentPagingBufferPrivateData; 
param.DmaBufferPrivateDataSize = CurrentPagingBufferPrivateDataSizeLeft; 
param.Operation = DXGK_OPERATION_TRANSFER; 
param.Transfer.Flags = 0; 
param.Transfer.TransferOffset = CurrentOffsetInAllocationBeingTransfered; 
param.Transfer.hAllocation = DriverContextForAllocationBeingMoved; 
param.Transfer.Source.SegmentId = 0; // Source is an MDL. 
param.Transfer.Source.pMdl = MDLDescribingPagesForAllocationBeingMoved; 
param.Transfer.Destination.SegmentId = 1; // Source to segment #1. 
param.Transfer.Destination.SegmentAddress = 0; // Source to offset 0 of segment #1.

// The driver receives MultipassOffset when it is initialized to zero 
// and uses it for multiple iterations of the paging operation.
//
param.MultipassOffset = 0;

do {
    // Call the driver's BuildPagingBuffer function to build a paging buffer.
    //
    ntStatus = BuildPagingBuffer(hAdapter, &param);
    // BuildPagingBuffer updates the size that is left in the 
    //  paging buffer with the amount of bytes that were written.
    //
    if (NT_SUCCESS(ntStatus)) {
        //
        // If STATUS_SUCCESS, batch the paging buffer to the 
        // scheduler after multiple paging operations are batched.
    }
    else if (ntStatus == STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER) {

        //
        // If STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, submit the current paging buffer to the scheduler to let 
        // the GPU start working on a partial transfer.
 
        VidSchSubmitPagingBuffer(CurrentPagingBuffer, CurrentPagingBufferSizeLeft);
 
        // Acquire a new paging buffer to complete the transfer.
        //
        VidMmAcquirePagingBuffer(&CurrentPagingBuffer, &CurrentPagingBufferSizeLeft);
    }
    else {
        //
        // A critical failure occurred, so bugcheck the system. 
        // This situation should never occur because the driver can 
        // fail the call only if it requires more DMA buffer space.
    }
} while(!NT_SUCCESS(ntStatus))

要求

要求
最低受支持的客户端 Windows Vista
目标平台 桌面
标头 d3dkmddi.h
IRQL PASSIVE_LEVEL

另请参阅

DXGKARG_BUILDPAGINGBUFFER

DxgkDdiAddDevice

DxgkDdiPatch

DxgkDdiSubmitCommand

pfnLockCb