window显示驱动开发—RecycleCreateCommandList
运行时会调用驱动程序的 RecycleCreateCommandList 函数,使之前未使用的 DDI 句柄重新完全有效。
这些回收 DDI 函数提供了优化机会,有助于回收小内存量命令列表的资源。 以下伪代码展示了通过从 API 到 DDI 的函数调用流程来实现运行时的情况:
::FinishCommandList()
{// Empty InterlockedSList, integrating into the cacheLoop { DC::pfnRecycleCommandList }If (Previously Destroyed CommandList Available){ IC::pfnRecycleCreateCommandList }else{IC::pfnCalcPrivateCommandListSizeIC::pfnCreateCommandListIC::pfnCalcDeferredContextHandleSize(D3D11DDI_HT_COMMANDLIST)}Loop { DC::pfnDestroy* (context-local handle destroy) }IC::pfnRecycleCreateDeferredContext
}
...
Sporadic: DC::pfnCreate* (context-local open during first-bind per CommandList)CommandList::Destroy()
{// If DC still alive, almost always recycle:If (DC still alive){ IC::pfnRecycleDestroyCommandList }Else{ IC::pfnDestroyCommandList }// Add to InterlockedSList
}
以下状态图显示了即时上下文 DDI 命令列表句柄的有效性。 绿色状态代表一个句柄,可与 CommandListExecute 一起使用。
1. 运行时与驱动协作流程
伪代码:命令列表生命周期(小内存量优化路径)
sequenceDiagramparticipant Appparticipant Runtimeparticipant DriverApp->>Runtime: ID3D11DeviceContext::FinishCommandListRuntime->>Driver: RecycleCreateCommandList (复用句柄)alt 缓存命中Driver->>Runtime: 返回已回收的hCommandListRuntime->>Driver: RecordCommands (记录新命令)Runtime->>Driver: RecycleCommandList (执行后回收)else 缓存未命中Driver->>Runtime: 返回E_OUTOFMEMORYRuntime->>Driver: CreateCommandList (创建新句柄)Runtime->>App: 返回失败或新句柄end
2. 驱动函数实现规范
RecycleCreateCommandList (关键回收路径)
HRESULT APIENTRY RecycleCreateCommandList(D3D11DDI_HDEFERREDCONTEXT hDeferredContext,D3D11DDI_HCOMMANDLIST* phCommandList
) {// 1. 从延迟上下文的线程本地缓存获取可复用句柄if (!hDeferredContext->commandListCache.empty()) {*phCommandList = hDeferredContext->commandListCache.back();hDeferredContext->commandListCache.pop_back();// 2. 重置内部状态(保留内存分配)ResetCommandListData(*phCommandList);return S_OK;}// 3. 缓存为空时直接返回错误(无回调)return E_OUTOFMEMORY;
}
RecycleCommandList (内存整合)
void APIENTRY RecycleCommandList(D3D11DDI_HCOMMANDLIST hCommandList,D3D11DDI_HDEFERREDCONTEXT hDeferredContext
) {// 1. 确保由正确的延迟上下文线程调用assert(GetCurrentThreadId() == hDeferredContext->threadId);// 2. 回收至线程本地缓存hDeferredContext->commandListCache.push_back(hCommandList);// 3. 可选:释放非必要内存(如大型临时缓冲区)TrimCommandListMemory(hCommandList);
}
3. 内存管理优化策略
缓存层级设计
缓存级别 | 内容 | 优化目标 |
---|---|---|
线程本地缓存 | 高频小命令列表(如CopyResource ) | 零分配、无锁操作 |
全局备用池 | 低频大命令列表 | 减少内存碎片 |
动态缓存大小调整
// 根据使用频率动态调整线程本地缓存大小
void AdjustCacheSize(D3D11DDI_HDEFERREDCONTEXT hContext) {const size_t currentSize = hContext->commandListCache.size();if (currentSize > hContext->maxCacheSize && hContext->hits > hContext->misses) {hContext->maxCacheSize *= 2; // 扩展缓存} else if (currentSize < hContext->maxCacheSize / 4) {hContext->maxCacheSize /= 2; // 收缩缓存}
}
4. 错误处理与调试
严格校验(调试构建)
#if DBG
void ValidateRecycledHandle(D3D11DDI_HCOMMANDLIST hCommandList) {if (IsHandleCorrupted(hCommandList)) {DebugBreak(); // 捕获重复释放或无效状态}
}
#endif
性能计数器
指标 | 采集方法 | 优化参考 |
---|---|---|
缓存命中率 | hits / (hits + misses) | >90% 表示优化有效 |
平均回收延迟 | QueryPerformanceCounter | 应 <1μs |
5. 线程模型与同步
无锁实现要求
线程本地缓存:每个延迟上下文维护独立的 std::vector 或环形缓冲区,无需同步。
全局池(可选):使用原子操作或无锁队列(如 mpsc_queue)。
禁止行为
跨线程访问缓存:若检测到非所属线程操作,触发调试层断言。
阻塞操作:禁止在回收路径中使用 malloc/free 或系统锁。
6. 总结
高频复用路径:
RecycleCreateCommandList 和 RecycleCommandList 构成零分配闭环。
低开销设计:线程本地缓存 + 无锁结构确保小命令列表的极致性能。
弹性扩展:动态缓存大小适应不同负载场景。