1. 每个GPU都至少维护着一个命令队列(本质上是环形缓冲区)。借助Direct3D API,CPU可利用,命令列表将命令提交到这个队列中去。当一系列命令被提交至命令队列之时,它们并不会被GPU立即执行,理解这一点至关重要。由于GPU可能正在处理先前插入命令队列内的命令,因此,后来新到的命令会一直在这个队列之中等待执行。
2. 当命令都被加入命令列表之后,我们必须调用ID3D12GraphicsCommandList::Close()方法来结束命令的记录。在调用ID3D12CommandQueu::ExecuteCommandLists()方法提交命令之前,一定要将其关闭。
3.记录在命令列表内的 命令,实际上时存储在与之关联的命令分配器上,当通过ID3D12CommandQueu::ExecuteCommandLists()方法执行命令列表的时候,命令队列就会引用分配器里的命令。
4. 我们可以 创建出多个关联于同一命令分配器的命令列表,但不能同时用它来记录命令。因此,当其中的一个命令列表在记录命令时,必须关闭同一命令分配器的其他命令列表。换句话说,要保证命令列表中所有命令都会按顺序连续地添加到命令分配器内。还要注意的一点是,当创建或重置一个命令列表,它会处于一种“打开 ”的状态。
5.在调用ID3D12CommandQueu::ExecuteCommandLists()方法之后,我们就可以通过ID3D12GraphicsCommandList::Reset()方法,安全地复用命令列表占用的相关底层内存来记录新的命令集。Reset()此方法将命令列表恢复为刚创建时的初始状态,我们可以借此复用其低层内存,也可以避免释放旧列表再创建新列表这一系列的繁琐操作。
6.注意,重置命令列表并不会影响命令队列中的命令,因为相关的命令分配器仍在维护着其内存中被命令队列引用的系列命令。
7.命令分配器ID3D12CommandAllocator::Reset(),使用大小归零,但是仍保持其当前的容量,由于命令队列可能引用命令分配器中的数据,所以在没有确定GPU执行完命令分配器中的所有命令之前,千万并不要重置命令分配器。
命令与多线程
1.命令列表并非自由线程对象。也就是说,多线程既不能同时共享相同的命令列表,也不能同时调用同一命令列表的方法。所以,每个线程通常都只使用各自的命令列表。
2.命令分配器亦不时 线程自由的对象。这就是说,多线程既不能同时共享同一个命令分配器,也不能同时调用同一命令分配器的方法。所以,每个线程一般都仅使用属于自己的命令分配器。
3.命令队列是线程自由对象,所以多线程可以同时访问同一命令队列,也能够同时调用它的方法。特别别是每个线程都能同时向命令队列提交它们自己所生成的命令列表。
4.出于性能的原因,应用程序必须在初始化期间,指出用于并行记录命令的命令列表最大数值。
创建命令列表
ThrowIfFailed(md3dDevice->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
mDirectCmdListAlloc.Get(), // Associated command allocator关联命令分配器
nullptr, // Initial PipelineStateObject 初始化流水线状态对象 (现在用不到)默认为NULL时,会自己设置虚拟的管道状态,这样做开销会低一点
IID_PPV_ARGS(mCommandList.GetAddressOf())));
创建命令分配器
ThrowIfFailed(md3dDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));
参数 type D3D12_COMMAND_LIST_TYPE
D3D12_COMMAND_LIST_TYPE_DIRECT = 0 指定 GPU 可执行的命令缓冲区。 直接命令列表不会继承任何 GPU 状态。
D3D12_COMMAND_LIST_TYPE_BUNDLE = 1, 指定只能通过直接命令列表执行的命令缓冲区。 捆绑命令列表继承当前设置的管道状态对象和基元拓扑) 之外的所有 GPU 状态 (。
D3D12_COMMAND_LIST_TYPE_COMPUTE = 2, 指定用于计算的命令缓冲区。
D3D12_COMMAND_LIST_TYPE_COPY = 3, 指定用于复制的命令缓冲区。
D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE = 4, 指定用于视频解码的命令缓冲区。
D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS = 5, 指定用于视频处理的命令缓冲区。
D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE = 6
创建命令队列
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
参数 D3D12_COMMAND_QUEUE_DESC
Type 指定 D3D12_COMMAND_LIST_TYPE的一个成员
Priority 命令队列的优先级,作为 D3D12_COMMAND_QUEUE_PRIORITY 枚举常量,用于选择普通或高优先级
Flags 指定 D3D12_COMMAND_QUEUE_FLAGS 枚举中的任何标志
NodeMask 对于单个GPU操作,请将此项设置为零。如果有多个 GPU 节点,请设置一个位来标识节点 (设备的物理适配器) 命令队列应用到该节点。 掩码中的每个位都对应一个节点。 必须仅设置 1 位。 必须仅设置 1 位。 请参阅 [多适配器系统](https://learn.microsoft.com/zh-cn/windows/win32/direct3d12/multi-engine)。
参数 Type D3D12_COMMAND_LIST_TYPE
D3D12_COMMAND_LIST_TYPE_DIRECT = 0, 指定 GPU 可执行的命令缓冲区。 直接命令列表不会继承任何 GPU 状态。
D3D12_COMMAND_LIST_TYPE_BUNDLE = 1,指定只能通过直接命令列表执行的命令缓冲区。 捆绑命令列表继承当前设置的管道状态对象和基元拓扑) 之外的所有 GPU 状态
D3D12_COMMAND_LIST_TYPE_COMPUTE = 2,指定用于计算的命令缓冲区。
D3D12_COMMAND_LIST_TYPE_COPY = 3,指定用于复制的命令缓冲区
D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE = 4,指定用于视频解码的命令缓冲区
D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS = 5,指定用于视频处理的命令缓冲区
D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE
参数 Priority D3D12_COMMAND_QUEUE_PRIORITY
D3D12_COMMAND_QUEUE_PRIORITY_NORMAL = 0 正常优先级
D3D12_COMMAND_QUEUE_PRIORITY_HIGH = 100 高优先级
D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME = 10000 全局实时优先级