PFND3DDDI_LOCKCB回调函数 (d3dumddi.h)
pfnLockCb 函数锁定分配,并从显示微型端口驱动程序或视频内存管理器获取指向分配的指针。
语法
PFND3DDDI_LOCKCB Pfnd3dddiLockcb;
HRESULT Pfnd3dddiLockcb(
HANDLE hDevice,
D3DDDICB_LOCK *unnamedParam2
)
{...}
参数
hDevice
显示设备的句柄 (图形上下文) 。
unnamedParam2
pData [in, out]
指向描述要锁定的分配 的D3DDDICB_LOCK 结构的指针。
返回值
pfnLockCb 返回以下值之一:
返回代码 | 说明 |
---|---|
S_OK | 已成功锁定分配。 |
D3DERR_NOTAVAILABLE | 光圈不可用。 |
D3DERR_WASSTILLDRAWING | 分配仍在用于呈现。 |
D3DDDIERR_CANTEVICTPINNEDALLOCATION | 由于无法取消重排光圈,并且由于已固定分配而无法逐出分配,因此无法锁定分配。 |
E_OUTOFMEMORY | 由于内存不足,pfnLockCb 无法完成 (当系统处于内存极低的情况下,并且没有足够的空间来分配) 页数组时,会出现这种情况。 |
E_INVALIDARG | 参数已验证并确定为不正确。 |
D3DDDIERR_DEVICEREMOVED | pfnLockCb 无法导致视频内存管理器和显示微型端口驱动程序执行适当的操作,因为发生了即插即用 (PnP) 停止或超时检测和恢复 (TDR) 事件。 通常,名为 pfnLockCb 的用户模式显示驱动程序函数 (Lock 或 ResourceMap 函数) 必须将此错误代码返回给 Direct3D 运行时。 Direct3D 版本 9 注意:有关返回错误代码的详细信息,请参阅 返回从运行时函数收到的错误代码。 Direct3D 版本 10 和 11 注意:如果驱动程序函数未返回值 (即返回参数类型为 VOID) ,则驱动程序函数调用 pfnSetErrorCb 函数以将错误代码发送回运行时。 有关处理错误代码的详细信息,请参阅 处理错误。 |
此函数还可能返回其他 HRESULT 值。
注解
用户模式显示驱动程序可以调用 Microsoft Direct3D 运行时的 pfnLockCb 函数来锁定分配,并从显示微型端口驱动程序或视频内存管理器获取指向分配的指针。 用户模式显示驱动程序通常调用 pfnLockCb ,以响应对其 Lock 或 ResourceMap 函数的调用, (或 ResourceMap 的其他变体(如 DynamicIABufferMapDiscard) )来锁定资源或资源中的图面。 在从 Lock 或 ResourceMap 调用返回之前,用户模式显示驱动程序必须先将资源或图面映射到相应的分配,然后调用 pfnLockCb 来锁定分配。 必须先锁定分配,然后才能从中读取或写入该分配,因为锁定:
- 保证分配的虚拟地址范围在锁定期间保持不变、有效、可读和可写。 视频内存管理器提供此保证。
- 提供一种将分配的读取和写入操作与分配的图形硬件访问同步的方法。 视频内存管理器和显示微型端口驱动程序执行同步。
用户模式显示驱动程序通常分别调用对应于其 Lock 和 Unlock 函数的每次调用的 pfnLockCb 和 pfnUnlockCb 函数,除非驱动程序处理在创建资源时在 D3DDDIARG_CREATERESOURCE 结构的 Flags 成员中设置动态位字段标志的资源。 运行时经常请求驱动程序锁定这些类型的资源,通常在 D3DDDIARG_LOCK 结构的 Flags 成员中设置了 NoOverwrite 位字段标志。 由于不应按照 NoOverwrite) 指示 (修改此类资源中的数据,因此为每个锁请求调用 pfnLockCb 会占用过多的处理时间。 为了防止为每个锁请求调用 pfnLockCb,驱动程序可以在调用其 Lock 函数并设置 NoOverwrite 位字段标志时,缓存在 D3DDDIARG_LOCK 的 pSurfData 成员中返回的虚拟内存指针。 但是,只要调用具有“放弃位字段”标志或未设置标志的 Lock 函数,驱动程序就可以继续调用 pfnLockCb。
Direct3D 版本 10 和 11 注意:
用户模式显示驱动程序通常调用 pfnLockCb 和 pfnUnlockCb 函数,这些函数对应于每次调用其 ResourceMap 和 ResourceUnmap 函数 (或这些函数的其他变体) 。 当驱动程序处理在D3D10DDIARG_CREATERESOURCE的 Usage 成员中设置了D3D10_DDI_USAGE_DYNAMIC值的资源时,或者在创建资源时D3D11DDIARG_CREATERESOURCE结构中设置D3D10_DDI_USAGE_DYNAMIC值时,不会发生这种情况。 运行时经常请求驱动程序锁定这些类型的资源,通常是在调用 ResourceMap 时将 D3D10_DDI_MAP_WRITE_NOOVERWRITE 值传递给 DDIMap 参数。 由于不应按照 D3D10_DDI_MAP_WRITE_NOOVERWRITE) 指示 (修改此类资源中的数据,因此为每个锁请求调用 pfnLockCb 会占用过多的处理时间。 为了防止为每个锁请求调用 pfnLockCb,驱动程序可以在使用 D3D10_DDI_MAP_WRITE_NOOVERWRITE 调用其 ResourceMap 函数时缓存它在 pMappedSubResource 参数中返回的虚拟内存指针。 但是,只要使用 D3D10_DDI_MAP_WRITE_DISCARD 值或 0 传递给 DDIMap 参数调用其 ResourceMap 函数,驱动程序就可以继续调用 pfnLockCb。
虽然应用程序不保留与虚拟内存指针关联的资源的未完成锁,但驱动程序通常会通过在驱动程序调用 pfnRenderCb 函数之前调用 pfnUnlockCb 函数来取消缓存 虚拟内存指针 。 如果未取消缓存锁,或者由于应用程序仍锁定资源而无法取消缓存锁,则硬件可能会从锁定的分配中呈现。 如果分配位于本地视频内存中,则视频内存管理器无法支持此操作模式;因此,当内存管理器检测到这种情况时,内存管理器会将分配逐出给系统内存或 AGP 内存。 如果系统或 AGP 内存段不支持分配,则内存管理器无法调用 pfnRenderCb 并D3DDDIERR_CANTRENDERLOCKEDALLOCATION。 因此,系统或 AGP 段应支持为响应创建资源而分配的顶点和索引缓冲区分配,这些资源在 D3DDDIARG_CREATERESOURCE (的 Flags 成员中设置动态位字段标志或在 D3D10DDIARG_CREATERESOURCE 或 D3D11DDIARG_CREATERESOURCE) 的 Usage 成员中设置D3D10_DDI_USAGE_DYNAMIC值。
在调用 pfnLockCb 时,在 D3DDDICB_LOCK的 Flags 成员中设置“放弃位字段”标志会导致视频内存管理器创建正在锁定的分配的新实例。 视频内存管理器通过将新句柄返回到 D3DDDICB_LOCK 的 hAllocation 成员中的用户模式显示驱动程序来表示新实例。
刷新其命令缓冲区后,用户模式显示驱动程序必须再次尝试使用 Discard 和 NoExistingReference 位字段标志锁定图面。 NoExistingReference 位字段标志向视频内存管理器指示驱动程序当前没有对在其命令缓冲区中排队锁定的任何分配实例的引用。 然后,视频内存管理器可以重复使用分配的任何实例来处理锁,包括当前实例。
调用设置了“放弃位字段”标志的 pfnLockCb 后,用户模式显示驱动程序应始终检查D3DDDICB_LOCK的 hAllocation 成员中的更新句柄值。 如果提供了新的分配句柄,则用户模式显示驱动程序应更新其内部数据结构以引用新的分配句柄。 用户模式显示驱动程序还应将锁定分配基址的重新编程版本添加到当前命令缓冲区 (,因为分配实例包含不同的基址) 。 视频内存管理器验证驱动程序使用的分配实例的使用,并拒绝错误地使用分配实例的 DMA 缓冲区, (即,如果错误地使用分配实例,则调用 pfnPresentCb 和 pfnRenderCb 会失败) 。 驱动程序引用分配的特定实例后,驱动程序无法再引用同一分配的上一个实例。 例如,如果命令缓冲区使用分配 A,并且当前使用实例 A0 和 A1,则只要使用 A1 (即立即出现在修补程序位置列表中,) A0 变为无效。 显示微型端口驱动程序可以生成同时引用 A0 和 A1 的修补程序位置列表。 但是,必须对引用进行排序 (即,可以先使用 A0;使用 A2 后,A0 将变为无效;使用 A2 时,A1 变为无效,) 依此。
用户模式显示驱动程序可能会调用 pfnLockCb 进行系统内存分配,即使内存尚未预先分配,因为显示微型端口驱动程序实际上可能正在通过 DMA 发送或以异步方式将这些分配传输到图形硬件。 因此,在允许应用程序写入图面之前,必须通知显示微型端口驱动程序和视频内存管理器,以便他们可以在必要时阻止锁定。
用户模式显示驱动程序还可以锁定分配的子区域。 当提供不显眼或线性化的硬件光圈时,通常不需要这种类型的锁,因为在这种情况下,用户模式显示驱动程序可以通过偏移指针将整个分配上的锁转换为子区域。 但是,当 pfnLockCb 因使用 D3DERR_NOTAVAILABLE 指示光圈不可用而失败时,内存管理器会请求用户模式显示驱动程序复制视频内存内容。 用户模式显示驱动程序会取消重排或线性化视频内存内容,同时将其复制到另一个内存区域。 在这种情况下,用户模式显示驱动程序可以提供要复制的页面列表,以在大型分配中锁定小子区域时节省大量复制。 请注意,如果用户模式显示驱动程序未在 D3DDDICB_LOCK 结构的Flags 成员中设置 LockEntire 位字段标志,并且未在 D3DDDICB_LOCK 的 pPages 成员中指定页列表,内存管理器将无法调用 pfnLockCb,D3DERR_NOTAVAILABLE。 如果用户模式显示驱动程序设置了 LockEntire 位字段标志,则它还必须将 D3DDDICB_LOCK 的 NumPages 和 pPages 成员分别设置为 0 和 NULL。 锁定使用永久后备存储创建的分配时,用户模式显示驱动程序应始终在 pPages 中提供页面列表。 在这种情况下,内存管理器使用页面列表将仅脏特定页面标记为,并且不需要在用于呈现时从后备存储复制整个分配。
用户模式显示驱动程序可以调用 pfnLockCb 来获取单个分配的多个重排范围 (例如,每个 MIP 级别的一个重排范围) 。 如果驱动程序无法获取任何一个范围,Direct3D 运行时将逐出整个分配来处理锁 () 的所有 MIP 级别,并回收所有重排范围。
当用户模式显示驱动程序请求将重排范围分配给分配时,驱动程序会有效地请求访问分配的未重排位。 对于此类请求,视频内存管理器将分配中的页面放入内存段,并设置一个重排范围来访问分配,或将分配中的页面逐出到内存段,然后逐出分配到系统内存,同时请求驱动程序在到系统内存的路上取消重排分配。 未重排到系统内存的分配在 GPU 再次使用该分配之前,将通过分页到视频内存) (重新重排。 因此,驱动程序无法通过在获取重排范围时设置 DonotWait 位字段标志) 来请求无覆盖类型锁 (。 同样,驱动程序无法在提交到 GPU (的 DMA 缓冲区中以这种方式引用分配锁,因为 DMA 缓冲区将被拒绝) 。
如果驱动程序必须以重排格式访问分配的位,则用户模式显示驱动程序可能会锁定重排分配而不获取重排范围。 在这种情况下,视频内存管理器为驱动程序提供指向分配的重排位的指针。 但是,驱动程序无法请求指向分配的重排位的指针,而未重排位的请求未完成,反之亦然, (即,分配上的锁当前挂起,) 获取重排范围。
在以下情况下,用户模式显示驱动程序应在 pfnLockCb 调用的 D3DDDICB_LOCK 的 Flags 成员中传递 Discard 位字段标志:
- 当 Direct3D 运行时在调用用户模式显示驱动程序的 Lock 函数时传递 D3DDDIARG_LOCK 结构的 Flags 成员中的 Discard 位字段标志时
- 当运行时在调用驱动程序的 ResourceMap 函数时,D3D10_DDI_MAP_WRITE_DISCARD值传递给 DDIMap 参数
下面的代码示例演示如何在调用 pfnLockCb 时使用 Discard 位字段标志。
HRESULT hr;
D3DDDICB_LOCK LockData;
LockData.hAllocation = AllocationToLock;
LockData.Flags.Discard = TRUE;
hr = pfnLockCb(&LockData)
if (FAILED(hr)) {
FlushAccumulatedCommandBufferToKernel();
LockData.Flags.Discard = TRUE;
LockData.Flags.NoExistingReference = TRUE;
hr = pfnLockCb(&LockData);
if (FAILED(hr)) {
// Fails the lock to the application
}
}
UpdateAllocationHandleInUMDDataStructure(LockData.hAllocation);
ProgramSurfaceBaseAddressInCurrentCommandBuffer(LockData.hAllocation);
要求
要求 | 值 |
---|---|
最低受支持的客户端 | 在 Windows Vista 和更高版本的 Windows 操作系统中可用。 |
目标平台 | 桌面 |
标头 | d3dumddi.h (包括 D3dumddi.h) |