 Code to create and compile a DirectML operator goes here. DML_BINDING_PROPERTIES executeDmlBindingProperties{ dmlCompiledOperator->GetBindingProperties() }; winrt::com_ptr<::IDMLOperatorInitializer> dmlOperatorInitializer; // Code to create a DirectML operator initializer goes here. DML_BINDING_PROPERTIES initializeDmlBindingProperties{ dmlOperatorInitializer->GetBindingProperties() }; UINT descriptorCount =...
The
descriptorCount value that you retrieve here determines the (minimum) size of the descriptor heap and of the binding table that you create in the next two steps.
DML_BINDING_PROPERTIES also contains a
TemporaryResourceSize member, which is the minimum size in bytes of the temporary resource that must be bound to the binding table for this dispatchable object. A value of zero means that a temporary resource is not required.
And a
PersistentResourceSize member, which is the minimum size in bytes of the persistent resource that must be bound to the binding table for this dispatchable object. A value of zero means that a persistent resource is not required. A persistent resource, if one is needed, must be supplied during initialization of a compiled operator (where it is bound as an output of the operator initializer) as well as during execution. There's more about this later in this topic. Only compiled operators have persistent resources—operator initializers always return a value of 0 for this member.
If you call IDMLDispatchable::GetBindingProperties on an operator initializer both before and after a call to IDMLOperatorInitializer::Reset, then the two sets of binding properties retrieved are not guaranteed to be identical.
Describe, create, and bind a descriptor heap
In terms of descriptors, your responsibility begins and ends with the descriptor heap itself. DirectML itself takes care of creating and managing the descriptors inside of the heap that you provide.
So, use a D3D12_DESCRIPTOR_HEAP_DESC structure to describe a heap large enough for the number of descriptors that the dispatchable needs. Then create it with ID3D12Device::CreateDescriptorHeap. And, lastly, call ID3D12GraphicsCommandList::SetDescriptorHeaps to bind your descriptor heap to the pipeline.
winrt::com_ptr<::ID3D12DescriptorHeap> d3D12DescriptorHeap; D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDescription{}; descriptorHeapDescription.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; descriptorHeapDescription.NumDescriptors = descriptorCount; descriptorHeapDescription.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; winrt::check_hresult( d3D12Device->CreateDescriptorHeap( &descriptorHeapDescription, _uuidof(d3D12DescriptorHeap), d3D12DescriptorHeap.put_void() ) ); std::array<ID3D12DescriptorHeap*, 1> d3D12DescriptorHeaps{ d3D12DescriptorHeap.get() }; d3D12GraphicsCommandList->SetDescriptorHeaps( static_cast<UINT>(d3D12DescriptorHeaps.size()), d3D12DescriptorHeaps.data() );
Describe and create a binding table
A DirectML binding table represents the resources that you bind to the pipeline for a dispatchable to use. Those resources could be input and output tensors (or other parameters) for an operator, or they could be various persistent and temporary resources that a dispatchable works with.
Use the DML_BINDING_TABLE_DESC structure to describe your binding table, including the dispatchable for which the binding table will represent the bindings, and the range of descriptors (from the descriptor heap that you just created) that you wish the binding table to refer to (and into which DirectML may write descriptors). The
descriptorCount value (one of the binding properties that we retrieved in the first step) tells us what minimum size is, in descriptors, of the binding table required for the dispatchable object. Here, we use that value to indicate the maximum number of descriptors that DirectML is permitted to write into our heap, from the start of both the supplied CPU and GPU descriptor handles.
Then call IDMLDevice::CreateBindingTable to create the DirectML binding table. In later steps, after we've created further resources for the dispatchable, we'll add those resources to the binding table.
Instead of passing a DML_BINDING_TABLE_DESC to this call, you can pass
nullptr, indicating an empty binding table.
DML_BINDING_TABLE_DESC dmlBindingTableDesc{}; dmlBindingTableDesc.Dispatchable = dmlOperatorInitializer.get(); dmlBindingTableDesc.CPUDescriptorHandle = d3D12DescriptorHeap->GetCPUDescriptorHandleForHeapStart(); dmlBindingTableDesc.GPUDescriptorHandle = d3D12DescriptorHeap->GetGPUDescriptorHandleForHeapStart(); dmlBindingTableDesc.SizeInDescriptors = descriptorCount; winrt::com_ptr<::IDMLBindingTable> dmlBindingTable; winrt::check_hresult( dmlDevice->CreateBindingTable( &dmlBindingTableDesc, __uuidof(dmlBindingTable), dmlBindingTable.put_void() ) );
The order in which DirectML writes descriptors into the heap is unspecified, so your application must take care not to overwrite the descriptors wrapped by the binding table. The supplied CPU and GPU descriptor handles may come from different heaps, however it is then your application's responsibility to ensure that the entire descriptor range referred to by the CPU descriptor handle is copied into the range referred to by the GPU descriptor handle prior to execution using this binding table. The descriptor heap from which the handles are supplied must have type D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV. Additionally, the heap referred to by the
GPUDescriptorHandle must be a shader-visible descriptor heap.
You can reset a binding table to remove any resources that you've added to it, while at the same time changing any property that you set on its initial DML_BINDING_TABLE_DESC (to wrap a new range of descriptors, or to re-use it for a different dispatchable). Just make the changes to the description structure, and call IDMLBindingTable::Reset.
dmlBindingTableDesc.Dispatchable = pIDMLCompiledOperator.get(); winrt::check_hresult( pIDMLBindingTable->Reset( &dmlBindingTableDesc ) );
Describe and bind any temporary/persistent resources
The DML_BINDING_PROPERTIES structure that we populated when we retrieved the binding properties of our dispatchable contains the size in bytes of any temporary and/or persistent resource that the dispatchable needs. If either of these sizes is non-zero, then create a Direct3D 12 buffer resource and add it to the binding table.
In the code example below, we create a temporary resource (
temporaryResourceSize bytes in size) for the dispatchable. We describe how we wish to bind the resource, and then we add that binding to the binding table.
Since we're binding a single buffer resource, we describe our binding with a DML_BUFFER_BINDING structure. In that structure, we specify the Direct3D 12 buffer resource (the resource must have dimension D3D12_RESOURCE_DIMENSION_BUFFER), as well as an offset-and-size into the buffer. It's also possible to describe a binding for an array of buffers (rather than for a single buffer), and the DML_BUFFER_ARRAY_BINDING structure exists for that purpose.
To abstract away the distinction between a buffer binding and a buffer array binding, we use the DML_BINDING_DESC structure. You can set the
Type member of the DML_BINDING_DESC to either DML_BINDING_TYPE_BUFFER or DML_BINDING_TYPE_BUFFER_ARRAY. And you can then set the
Desc member to point to either a DML_BUFFER_BINDING or to a DML_BUFFER_ARRAY_BINDING, depending on
Type.
We're dealing with the temporary resource in this example, so we add it to the binding table with a call to IDMLBindingTable::BindTemporaryResource.
D3D12_HEAP_PROPERTIES defaultHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT) }; winrt::com_ptr<::ID3D12Resource> temporaryBuffer; D3D12_RESOURCE_DESC temporaryBufferDesc{ CD3DX12_RESOURCE_DESC::Buffer(temporaryResourceSize) }; winrt::check_hresult( d3D12Device->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &temporaryBufferDesc, D3D12_RESOURCE_STATE_COMMON, nullptr, __uuidof(temporaryBuffer), temporaryBuffer.put_void() ) ); DML_BUFFER_BINDING bufferBinding{ temporaryBuffer.get(), 0, temporaryResourceSize }; DML_BINDING_DESC bindingDesc{ DML_BINDING_TYPE_BUFFER, &bufferBinding }; dmlBindingTable->BindTemporary