这里是SunshineBooming,GPU公司一枚小小的Driver工程师,主要工作是写DirectX12 Driver,我会持续更新这个DX12 Spec系列,可能比较冷门,但是都是干货和工作中的心得体会,有任何GPU相关的问题都可以在评论区互动,知无不言:
目录
1. Command List
1.1 基本概念
- What Command List?
A command list is a set of commands which the GPU executes,展开说,就是DX12向GPU下发命令不是立即执行的,所有通过API给GPU下发的命令,都会缓存到Command List中,最后再通过API下发执行Command List这样一个命令,执行之前缓存的所有命令。 - Why Command List?
答案是提升硬件效率,CPU将所有Command缓存起来集中发送到GPU,是优于DX10/11那种随时下发执行方式(Immediate Context)。
1.2 Direct Command List & Bundle
- Direct Command List: A direct command list corresponds to a command buffer which the GPU can execute, and dose not inherit any GPU state. Aka transient command Lists(TCLs)
- Bundle: A bundle can be executed only directly via a direct command list and inherit all GPU state. Aka persistent command lists(PCLs).
- 怎么理解Bundle?
Direct Command List容易理解,就是可以普通的,GPU可以直接执行的Command列表。
Bundle其实也好理解,我们在重复渲染每一帧画面的时候,发现有一些基本命令是可以重复使用的,我们把这些重复使用的命令打包成一个Bundle,让编译器预编译成GPU可以直接执行的命令,这样可以提升硬件效率。
2. Command Allocator
- Command allocator corresponds to the underlying allocations in which GPU commands are stored.
前文提到了,Command需要缓存到Command List中的,这个缓存区域就是Command Allocator,这个其实对应于GPU Driver中的Command Pool,是事先分配好的一块GPU Memory(一般Size可以动态增长)。
3. Command Queue
- A command queue corresponds to a queue of command lists which the GPU will execute.
Command Queue类似于一个调度器,如果开发者有多个渲染任务(比如说那种双屏的对战游戏),那么我们可以创建1个Command Queue + 2个Command List,Command List对应2个渲染任务,将其都挂到同一个Command Queue下面进行调度。
4. 小结
- 可以下图概括Command List、Command Allocator、Command Queue之间的关系。其实总结起来就是:Command List记录Command,Command Allocator分配GPU Memory,Command Queue调度Command List:
5. API
5.1 Command Allocator
HRESULT CreateCommandAllocator(
[in] D3D12_COMMAND_LIST_TYPE type,
REFIID riid,
[out] void **ppCommandAllocator
);
- 这里主要是用来创建Command Allocator,即存储Command所需要的GPU Memory,给到GPU Driver里面其实就是分配一块Command Buffer内存,一般是64KB对齐、动态增长的。
ID3D12CommandAllocator::Reset()
- 重置Command Allocator对应的GPU Memory,需要注意的是,对应的Command List需要处于close状态,即结束Record Command过程,否则Reset Command Allocator会返回Runtime错误。
5.2 Command Queue
HRESULT CreateCommandQueue(
const D3D12_COMMAND_QUEUE_DESC *pDesc,
REFIID riid,
void **ppCommandQueue
);
typedef struct D3D12_COMMAND_QUEUE_DESC {
D3D12_COMMAND_LIST_TYPE Type;
INT Priority;
D3D12_COMMAND_QUEUE_FLAGS Flags;
UINT NodeMask;
} D3D12_COMMAND_QUEUE_DESC;
- 这里要注意的点就是Command List&Queue&Allocator三者创建的时候,需要Type保持一致,要不然Command List与Command Allocator关联、Command Queue调度Command List时DX12 Runtime都会报错,当然报错的信息打印也都非常清晰。
5.3 Command List
HRESULT CreateCommandList(
[in] UINT nodeMask,
[in] D3D12_COMMAND_LIST_TYPE type,
[in] ID3D12CommandAllocator *pCommandAllocator,
[in, optional] ID3D12PipelineState *pInitialState,
[in] REFIID riid,
[out] void **ppCommandList
);
- 创建完Command Allocator后,可以创建Command List并与之关联。
- 这里要注意的点就是Command List&Queue&Allocator三者创建的时候,需要Type保持一致,要不然Command List与Command Allocator关联、Command Queue调度Command List时DX12 Runtime都会报错。
ID3D12GraphicsCommandList::Close()
- 结束Command List的Record状态,在进入到Close状态之后,Command List才能提交给Command Queue执行。
- 需要注意的是,Command List不能进行Close->Close的重复设置,否则Runtime会报错。
ID3D12GraphicsCommandList::Reset()
(
[in] ID3D12CommandAllocator *pAllocator,
[in, optional] ID3D12PipelineState *pInitialState
);
- 重置Command List到初始状态,一般Command List状态Init->Record->Close->Submit->Reset这样循环使用。
- 需要注意的是,Command List在Reset之前必须要进入Close状态,否则Runtime会报错。
5.4 小结
- 以上列出的都是Command List & Allocator & Queue的一些基本的API,还有更多的API太多了就没有一一列举了。