Bindung in DirectML
In DirectML bezieht sich die Bindung auf die Anlage von Ressourcen an die Pipeline für die GPU, die während der Initialisierung und Ausführung Ihrer Machine Learning-Operatoren verwendet werden soll. Diese Ressourcen können z. B. Eingabe- und Ausgabe-Tensoren sowie alle temporären oder persistenten Ressourcen sein, die der Operator benötigt.
In diesem Thema werden die konzeptionellen und verfahrenstechnischen Details der Bindung behandelt. Es wird empfohlen, die Dokumentation für die APIs, die Sie aufrufen, einschließlich Parametern und Anmerkungen, vollständig zu lesen.
Wichtige Ideen beim Binden
Die nachstehende Liste der Schritte enthält eine allgemeine Beschreibung bindungsbezogener Aufgaben. Sie müssen diese Schritte jedes Mal ausführen, wenn Sie eine Dispatchable ausführen - eine Dispatchable ist entweder ein Operator-Initialisierer oder ein kompilierter Operator. Diese Schritte führen die wichtigen Ideen, Strukturen und Methoden ein, die an der DirectML-Bindung beteiligt sind.
Die nachfolgenden Abschnitten dieses Themas enthalten ausführliche Infos und erläutern diese Bindungsaufgaben detaillierte, wobei anschauliche Codeschnipsel aus dem Codebeispiel der minimalen DirectML-Anwendung genommen werden.
- Rufen Sie IDMLDispatchable::GetBindingProperties für das Dispatchable auf, um zu bestimmen, wie viele Deskriptoren es benötigt und wie hoch der Bedarf an temporären/persistenten Ressourcen ist.
- Erstellen Sie einen Direct3D 12-Deskriptorheap groß genug für die Deskriptoren, und binden Sie ihn an die Pipeline.
- Rufen Sie IDMLDevice::CreateBindingTable auf, um eine DirectML-Bindungstabelle zu erstellen, um die an die Pipeline gebundenen Ressourcen darzustellen. Verwenden Sie die DML_BINDING_TABLE_DESC Struktur, um Ihre Bindungstabelle zu beschreiben, einschließlich der Teilmenge der Deskriptoren, auf die sie im Deskriptorheap verweist.
- Erstellen Sie temporäre/persistente Ressourcen als Direct3D 12-Pufferungsressourcen, beschreiben Sie es mit DML_BUFFER_BINDING und DML_BINDING_DESC-Strukturen, und fügen Sie es der Bindungstabelle hinzu.
- Wenn das Dispatchable ein kompilierter Operator ist, erstellen Sie einen Puffer von Tensor-Elementen als Direct3D 12-Pufferressource. Auffüllen/Hochladen, beschreiben sie es mit DML_BUFFER_BINDING und DML_BINDING_DESC-Strukturen, und fügen Sie es der Bindungstabelle hinzu.
- Übergeben Sie Ihre Bindungstabellen als Parameter, wenn Sie IDMLCommandRecorder::RecordDispatch aufrufen.
Abrufen der Bindungseigenschaften eines Dispatchable
Die DML_BINDING_PROPERTIES-Struktur beschreibt die Bindungsanforderungen eines Dispatchables (Operator-Initialisierer oder kompilierter Operator). Diese bindungsbezogenen Eigenschaften enthalten die Anzahl der Deskriptoren, die Sie an das Dispatchable binden sollten, sowie die Größe in Byte aller temporären und/oder persistenten Ressourcen, die es benötigt.
Hinweis
Selbst für mehrere Operatoren desselben Typs machen Sie keine Annahmen darüber, dass sie dieselben Bindungsanforderungen haben. Fragen Sie die Bindungseigenschaften für jeden von Ihnen erstellten Initialisierer und Operator ab.
Rufen Sie IDMLDispatchable::GetBindingProperties auf, um eine DML_BINDING_PROPERTIES abzurufen.
winrt::com_ptr<::IDMLCompiledOperator> dmlCompiledOperator;
// 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 = ...
Der hier abgerufene descriptorCount
Wert bestimmt die (minimale) Größe des Deskriptorheap und der Bindungstabelle, die Sie in den nächsten beiden Schritten erstellen.
DML_BINDING_PROPERTIES enthält auch ein TemporaryResourceSize
Element, bei dem es sich um die minimale Größe in Byte der temporären Ressource handelt, die an die Bindungstabelle für dieses jederzeit verfügbare Objekt gebunden werden muss. Ein Wert von Null bedeutet, dass eine temporäre Ressource nicht erforderlich ist.
Und ein PersistentResourceSize
Element, bei dem es sich um die minimale Größe in Byte der persistenten Ressource handelt, die an die Bindungstabelle für dieses jederzeit verfügbare Objekt gebunden werden muss. Ein Wert von Null bedeutet, dass eine persistente Ressource nicht erforderlich ist. Eine persistente Ressource muss bei Bedarf während der Initialisierung eines kompilierten Operators (wo sie als Ausgabe des Operator-Initialisierers gebunden ist) sowie während der Ausführung bereitgestellt werden. Weitere Informationen hierzu finden Sie weiter unten in diesem Thema. Nur kompilierte Operatoren verfügen über persistente Ressourcen– Operator-Initialisierer geben immer den Wert 0 für dieses Element zurück.
Wenn Sie IDMLDispatchable::GetBindingProperties sowohl vor als auch nach einem Aufruf vonIDMLOperatorInitializer::Reset aufrufen, sind die beiden abgerufenen Bindungseigenschaften nicht garantiert identisch.
Beschreiben, Erstellen und Binden eines Deskriptorheap
In Bezug auf Deskriptoren beginnt und endet Ihre Verantwortung mit dem Deskriptorheap selbst. DirectML selbst kümmert sich um das Erstellen und Verwalten der Deskriptoren innerhalb des von Ihnen bereitgestellten Deskriptorheap.
Verwenden Sie daher eine D3D12_DESCRIPTOR_HEAP_DESC-Struktur, um einen Heap zu beschreiben, der groß genug ist, um die Anzahl der Deskriptoren zu beschreiben, die das Dispatchable erfordert. Erstellen Sie es dann mit ID3D12Device::CreateDescriptorHeap. Rufen Sie schließlich ID3D12GraphicsCommandList::SetDescriptorHeaps auf, um den Deskriptorheap an die Pipeline zu binden.
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()
);
Beschreiben und Erstellen einer Bindungstabelle
Eine DirectML-Bindungstabelle stellt die Ressourcen dar, die Sie für ein zu verwendender Dispatchable an die Pipeline binden. Diese Ressourcen können Eingabe- und Ausgabe-Tensoren (oder andere Parameter) für einen Operator sein, oder sie können verschiedene persistente und temporäre Ressourcen sein, mit denen ein Dispatchable funktioniert.
Verwenden Sie die DML_BINDING_TABLE_DESC-Struktur, um Ihre Bindungstabelle zu beschreiben, einschließlich des Dispatchable, für das die Bindungstabelle die Bindungen darstellt, und den Bereich der Deskriptoren (vom soeben erstellten Deskriptorheap), auf den die Bindungstabelle verweisen soll (und in die DirectML Deskriptoren schreiben kann). Der descriptorCount
Wert (eine der Bindungseigenschaften, die wir im ersten Schritt abgerufen haben) teilt uns mit, welche Mindestgröße in Deskriptoren der Bindungstabelle für das jederzeit verfügbare Objekt erforderlich ist. Hier verwenden wir diesen Wert, um die maximale Anzahl von Deskriptoren anzugeben, die DirectML vom Anfang der bereitgestellten CPU- und GPU-Deskriptorhandles in unseren Heap schreiben darf.
Rufen Sie dann IDMLDevice::CreateBindingTable auf, um die DirectML-Bindungstabelle zu erstellen. In späteren Schritten fügen wir diese Ressourcen der Bindungstabelle hinzu, nachdem wir weitere Ressourcen für das Dispatchable erstellt haben.
Anstatt einen DML_BINDING_TABLE_DESC an diesen Aufruf zu übergeben, können Sie eine leere Bindungstabelle übergeben nullptr
.
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()
)
);
Die Reihenfolge, in der DirectML Deskriptoren in den Heap schreibt, ist nicht angegeben, sodass Ihre Anwendung darauf achten muss, die Deskriptoren, die von der Bindungstabelle eingeschlossen sind, nicht zu überschreiben. Die bereitgestellten CPU- und GPU-Deskriptorhandles können aus verschiedenen Heaps stammen. Es liegt jedoch in der Verantwortung Ihrer Anwendung, sicherzustellen, dass der gesamte vom CPU-Deskriptorhandle referenzierte Deskriptorbereich vor der Ausführung mithilfe dieser Bindungstabelle in den Bereich kopiert wird, auf den der GPU-Deskriptorhandle verweist. Der Deskriptorheap, aus dem die Handles bereitgestellt werden, muss von Typ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV sein. Darüber hinaus muss der Heap, auf den GPUDescriptorHandle
verweist, ein Shader-sichtbarer Deskriptorheap sein.
Sie können eine Bindungstabelle zurücksetzen, um alle Ressourcen zu entfernen, die Sie ihm hinzugefügt haben, und gleichzeitig jede Eigenschaft ändern, die Sie für die anfängliche DML_BINDING_TABLE_DESC festgelegt haben (um einen neuen Bereich von Deskriptoren zu umschließen oder sie für ein anderes Dispatchable erneut zu verwenden). Nehmen Sie einfach die Änderungen an der Beschreibungsstruktur vor, und rufen Sie IDMLBindingTable::Reset auf.
dmlBindingTableDesc.Dispatchable = pIDMLCompiledOperator.get();
winrt::check_hresult(
pIDMLBindingTable->Reset(
&dmlBindingTableDesc
)
);
Beschreiben und Binden temporärer/persistenter Ressourcen
Die DML_BINDING_PROPERTIES-Struktur, die beim Abrufen der Bindungseigenschaften unseres Dispatchable aufgefüllt wurde, enthält die Größe in Byte aller temporären und/oder persistenten Ressourcen, die das Dispatchable benötigt. Wenn eine dieser Größen ungleich Null ist, erstellen Sie eine Direct3D 12-Pufferressource, und fügen Sie sie der Bindungstabelle hinzu.
Im folgenden Codebeispiel erstellen wir eine temporäre Ressource (temporaryResourceSize
Bytes in Größe) für das Dispatchable. Wir beschreiben, wie wir die Ressource binden möchten, und dann fügen wir diese Bindung zur Bindungstabelle hinzu.
Da wir eine einzelne Pufferressource binden, beschreiben wir unsere Bindung mit einer DML_BUFFER_BINDING-Struktur. In dieser Struktur geben wir die Direct3D 12-Pufferressource an (die Ressource muss Dimension D3D12_RESOURCE_DIMENSION_BUFFER haben), sowie ein Offset und eine Größe in den Puffer. Es ist auch möglich, eine Bindung für eine Matrix von Puffern (und nicht für einen einzelnen Puffer) zu beschreiben, und die DML_BUFFER_ARRAY_BINDING-Struktur ist zu diesem Zweck vorhanden.
Um den Unterschied zwischen einer Pufferbindung und einer Puffer-Matrixbindung abstrahieren zu können, verwenden wir die DML_BINDING_DESC-Struktur. Sie können das Type
Element der DML_BINDING_DESC entweder auf DML_BINDING_TYPE_BUFFER oder DML_BINDING_TYPE_BUFFER_ARRAY festlegen. Anschließend können Sie das Desc
Element je nach Type
DML_BUFFER_ARRAY_BINDING auf ein DML_BUFFER_BINDING oder auf ein DML_BUFFER_ARRAY_BINDING zeigen.
Wir behandeln die temporäre Ressource in diesem Beispiel. Daher fügen wir sie der Bindungstabelle mit einem Aufruf von IDMLBindingTable::BindTemporaryResource hinzu.
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->BindTemporaryResource(&bindingDesc);
Bei einer temporären Ressource (falls erforderlich) handelt es sich um einen Entwurfsspeicher, der während der Ausführung des Operators intern verwendet wird, sodass Sie sich nicht mit dem Inhalt befassen müssen. Sie müssen sie auch nicht beibehalten, nachdem ihr Aufruf von IDMLCommandRecorder::RecordDispatch für die GPU abgeschlossen wurde. Dies bedeutet, dass Ihre Anwendung die temporäre Ressource zwischen Dispatches des kompilierten Operators freigeben oder überschreiben kann. Der bereitgestellte Pufferbereich, der gebunden werden soll, da die temporäre Ressource den Anfangsoffset an DML_TEMPORARY_BUFFER_ALIGNMENT ausrichten muss. Der Typ des Heaps, der dem Puffer zugrunde liegt, muss D3D12_HEAP_TYPE_DEFAULT sein.
Wenn das Dispatch jedoch eine Nicht-Null-Größe für ihre langlebigere persistente Ressource meldet, ist das Verfahren etwas anders. Sie sollten einen Puffer erstellen und eine Bindung nach demselben Muster beschreiben wie oben gezeigt. Fügen Sie sie jedoch der Bindungstabelle des Operator-Initialisierers mit einem Aufruf an IDMLBindingTable::BindOutputs hinzu, da es der Job des Operator-Initialisierers ist, die persistente Ressource zu initialisieren. Fügen Sie sie dann der Bindungstabelle des kompilierten Operators mit einem Aufruf an IDMLBindingTable::BindPersistentResource hinzu. Sehen Sie sich das Codebeispiel für die minimale DirectML-Anwendung an, um diesen Workflow in Aktion zu sehen. Der Inhalt und die Lebensdauer der persistenten Ressource müssen beibehalten werden, solange der kompilierte Operator ausgeführt wird. Das heißt, wenn ein Operator eine persistente Ressource erfordert, muss die Anwendung sie während der Initialisierung bereitstellen und sie anschließend auch für alle zukünftigen Ausführungen des Operators bereitstellen, ohne deren Inhalt zu ändern. Die persistente Ressource wird in der Regel von DirectML verwendet, um Nachschlagetabellen oder andere langlebige Daten zu speichern, die während der Initialisierung eines Operators berechnet und bei zukünftigen Ausführungen dieses Operators wiederverwendet werden. Der bereitgestellte Pufferbereich, der gebunden werden soll, da der persistente Puffer seinen Startoffset an DML_PERSISTENT_BUFFER_ALIGNMENT ausgerichtet haben muss. Der Typ des Heaps, der dem Puffer zugrunde liegt, muss D3D12_HEAP_TYPE_DEFAULT sein.
Beschreiben und Binden von Tensoren
Wenn Sie mit einem kompilierten Operator (statt mit einem Operator-Initialisierer) arbeiten, müssen Sie Eingabe- und Ausgaberessourcen (für Tensoren und andere Parameter) an die Bindungstabelle des Operators binden. Die Anzahl der Bindungen muss genau mit der Anzahl der Eingaben des Operators übereinstimmen, einschließlich optionaler Tensoren. Die spezifischen Eingabe- und Ausgabe-Tensoren und andere Parameter, die ein Operator verwendet, werden im Thema für diesen Operator dokumentiert (z . B. DML_ELEMENT_WISE_IDENTITY_OPERATOR_DESC).
Eine Tensor-Ressource ist ein Puffer, der die einzelnen Elementwerte des Tensors enthält. Sie laden einen solchen Puffer mithilfe der regulären Direct3D 12-Techniken (Hochladen von Ressourcen und Lesen von Daten über einen Puffer) in/aus der GPU hoch und lesen diesen zurück. Sehen Sie sich das Codebeispiel für die minimale DirectML-Anwendung an, um diese Techniken in Aktion zu sehen.
Beschreiben Sie schließlich die Eingabe- und Ausgabe-Ressourcenbindungen mit DML_BUFFER_BINDING und DML_BINDING_DESC-Strukturen, und fügen Sie sie dann der Bindungstabelle des kompilierten Operators mit Aufrufen an IDMLBindingTable::BindInputs und IDMLBindingTable::BindOutputs hinzu. Wenn Sie eine IDMLBindingTable::Bind*-Methode aufrufen, schreibt DirectML einen oder mehrere Deskriptoren in den Bereich der CPU-Deskriptoren.
DML_BUFFER_BINDING inputBufferBinding{ inputBuffer.get(), 0, tensorBufferSize };
DML_BINDING_DESC inputBindingDesc{ DML_BINDING_TYPE_BUFFER, &inputBufferBinding };
dmlBindingTable->BindInputs(1, &inputBindingDesc);
DML_BUFFER_BINDING outputBufferBinding{ outputBuffer.get(), 0, tensorBufferSize };
DML_BINDING_DESC outputBindingDesc{ DML_BINDING_TYPE_BUFFER, &outputBufferBinding };
dmlBindingTable->BindOutputs(1, &outputBindingDesc);
Einer der Schritte beim Erstellen eines DirectML-Operators (siehe IDMLDevice::CreateOperator) besteht darin, eine oder mehrere DML_BUFFER_TENSOR_DESC-Strukturen zu deklarieren, um die vom Operator benötigten und zurückgegebenen Tensor-Datenpuffer zu beschreiben. Neben dem Typ und der Größe des Tensor-Puffers können Sie optional das DML_TENSOR_FLAG_OWNED_BY_DML Flag angeben.
DML_TENSOR_FLAG_OWNED_BY_DML gibt an, dass die Tensordaten im Besitz und von DirectML verwaltet werden sollen. DirectML erstellt eine Kopie der Tensordaten während der Initialisierung des Operators und speichert sie in der persistenten Ressource. Dadurch kann DirectML die Neuformatierung der Tensordaten in andere, effizientere Formulare durchführen. Das Festlegen dieses Flags kann die Leistung erhöhen, ist aber in der Regel nur für Tensoren nützlich, deren Daten sich nicht für die Lebensdauer des Operators ändern (z. B. Gewichtungs-Tensoren). Und das Kennzeichen kann nur für Eingabe-Tensoren verwendet werden. Wenn die Kennzeichnung für eine bestimmte Tensorbeschreibung festgelegt wird, muss der entsprechende Tensor während der Operator-Initialisierung an die Bindungstabelle gebunden werden, und nicht während der Ausführung (was zu einem Fehler führt). Das ist das Gegenteil des Standardverhaltens (das Verhalten ohne das DML_TENSOR_FLAG_OWNED_BY_DML Flag), bei dem der Tensor während der Ausführung und nicht während der Initialisierung gebunden werden soll. Alle an DirectML gebundenen Ressourcen müssen DEFAULT- oder CUSTOM-Heap-Ressourcen sein.
Weitere Informationen finden Sie unter IDMLBindingTable::BindInputs und IDMLBindingTable::BindOutputs.
Ausführen des Dispatchable
Übergeben Sie Ihre Bindungstabellen als Parameter, wenn Sie IDMLCommandRecorder::RecordDispatch aufrufen.
Wenn Sie die Bindungstabelle während eines Aufrufs von IDMLCommandRecorder::RecordDispatch verwenden, bindet DirectML die entsprechenden GPU-Deskriptoren an die Pipeline. Die CPU- und GPU-Deskriptorhandle müssen nicht auf die gleichen Einträge in einem Deskriptorheap verweisen, es liegt jedoch in der Verantwortung Ihrer Anwendung, sicherzustellen, dass der gesamte vom CPU-Deskriptorhandle referenzierte Deskriptorbereich vor der Ausführung mithilfe dieser Bindungstabelle in den Bereich kopiert wird, auf den der GPU-Deskriptorhandle verweist.
winrt::com_ptr<::ID3D12GraphicsCommandList> d3D12GraphicsCommandList;
// Code to create a Direct3D 12 command list goes here.
winrt::com_ptr<::IDMLCommandRecorder> dmlCommandRecorder;
// Code to create a DirectML command recorder goes here.
dmlCommandRecorder->RecordDispatch(
d3D12GraphicsCommandList.get(),
dmlOperatorInitializer.get(),
dmlBindingTable.get()
);
Schließen Sie schließlich Ihre Direct3D 12-Befehlsliste, und übermitteln Sie sie für die Ausführung wie jede andere Befehlsliste.
Vor der Ausführung von RecordDispatch auf der GPU müssen Sie alle gebundenen Ressourcen in den D3D12_RESOURCE_STATE_UNORDERED_ACCESS-Zustand oder auf einen Zustand übertragen, der implizit auf D3D12_RESOURCE_STATE_UNORDERED_ACCESS verfügbar ist, z . B. D3D12_RESOURCE_STATE_COMMON. Nach Abschluss dieses Aufrufs bleiben die Ressourcen im D3D12_RESOURCE_STATE_UNORDERED_ACCESS-Zustand. Die einzige Ausnahme hierfür sind Upload-Heaps, die gebunden werden wenn ein Operator-Initialisierer ausgeführt wird und ein oder mehrere Tensoren die DML_TENSOR_FLAG_OWNED_BY_DML-Flag festgelegt haben. In diesem Fall müssen alle Upload-Heaps, die für die Eingabe gebunden sind, im Zustand D3D12_RESOURCE_STATE_GENERIC_READ sein und in diesem Zustand bleiben, wie von allen Upload-Heaps gefordert. Wenn DML_EXECUTION_FLAG_DESCRIPTORS_VOLATILE beim Kompilieren des Operators nicht festgelegt wurde, müssen alle Bindungen für die Bindungstabelle festgelegt werden, bevor RecordDispatch aufgerufen wird, andernfalls ist das Verhalten nicht definiert. Wenn ein Operator eine späte Bindung unterstützt, kann die Bindung von Ressourcen zurückgestellt werden, bis die Direct3D 12-Befehlsliste zur Ausführung an die Befehlswarteschlange übermittelt wird.
RecordDispatch fungiert logisch wie ein Aufruf von ID3D12GraphicsCommandList::Dispatch. Daher sind ungeordnete Zugriffsansichtsbarrieren (UAV) erforderlich, um die richtige Sortierung sicherzustellen, wenn Datenabhängigkeiten zwischen Dispatches vorhanden sind. Diese Methode fügt keine UAV-Barrieren für Eingabe- oder Ausgaberessourcen ein. Ihre Anwendung muss sicherstellen, dass die richtigen UAV-Barrieren für alle Eingaben ausgeführt werden, wenn ihre Inhalte von einem Upstream-Dispatch abhängen, und von allen Ausgaben, wenn Upstream-Dispatches vorhanden sind, die von diesen Ausgaben abhängen.
Lebensdauer und Synchronisierung von Deskriptoren und Bindungstabellen
Ein gutes mentales Bindungsmodell in DirectML besteht darin, dass hinter den Kulissen die DirectML-Bindungstabelle selbst Deskriptoren mit unsortierten Zugriffsansichten (UAV) innerhalb des von Ihnen bereitgestellten Deskriptorheap erstellt und verwaltet. Daher gelten alle üblichen Direct3D 12-Regeln für die Synchronisierung des Zugriffs auf diesen Heap und deren Deskriptoren. Es liegt in der Verantwortung Ihrer Anwendung, die korrekte Synchronisierung zwischen CPU und GPU durchzuführen, die eine Bindungstabelle verwendet.
Eine Bindungstabelle kann einen Deskriptor nicht überschreiben, während der Deskriptor verwendet wird (z. B. durch einen vorherigen Rahmen). Wenn Sie also einen bereits gebundenen Deskriptorheap wiederverwenden möchten (z. B. durch erneutes Aufrufen von Bindung* in einer Bindungstabelle, die darauf verweist, oder durch manuelles Überschreiben des Deskriptors) sollten Sie auf das Dispatchable warten, das derzeit den Deskriptorheap verwendet, um die Ausführung auf der GPU abzuschließen. Eine Bindungstabelle hat keinen starken Verweis auf den Deskriptorheap, in den sie schreibt, sodass Sie den sicherungsbasierten Shader-sichtbaren Deskriptorheap erst freigeben müssen, wenn alle Arbeiten mit dieser Bindungstabelle die Ausführung der GPU abgeschlossen haben.
Während eine Bindungstabelle hingegen einen Deskriptorheap angibt und verwaltet, enthält die Tabelle keinen dieser Speicher. Sie können also jederzeit eine Bindungstabelle freigeben oder zurücksetzen, nachdem Sie IDMLCommandRecorder::RecordDispatch damit aufgerufen haben (Sie müssen nicht warten, bis dieser Aufruf für die GPU abgeschlossen ist, solange die zugrunde liegenden Deskriptoren gültig sind).
Die Bindungstabelle behält keine starken Verweise auf Ressourcenbindungen bei. Ihre Anwendung muss sicherstellen, dass Ressourcen nicht gelöscht werden, während sie von der GPU weiterhin verwendet werden. Außerdem ist eine Bindungstabelle nicht threadsicher – Ihre Anwendung darf keine Methoden für eine Bindungstabelle gleichzeitig aus verschiedenen Threads ohne Synchronisierung aufrufen.
Und denken Sie daran, dass in jedem Fall eine erneute Bindung nur erforderlich ist, wenn Sie ändern, welche Ressourcen gebunden sind. Wenn Sie die gebundenen Ressourcen nicht ändern müssen, können Sie beim Start einmal binden und bei jedem Aufruf von RecordDispatch dieselbe Bindungstabelle übergeben.
Stellen Sie für überlappendes Machine Learning- und Renderingworkloads einfach sicher, dass die Bindungstabellen jedes Rahmens auf Bereiche des Deskriptorsheap verweisen, die noch nicht auf der GPU verwendet werden.
Optional angeben von spät gebundenen Operatorbindungen
Wenn Sie mit einem kompilierten Operator (statt mit einem Operator-Initialisierer) arbeiten, haben Sie die Möglichkeit, eine späte Bindung für den Operator anzugeben. Ohne späte Bindung müssen Sie alle Bindungen in der Bindungstabelle festlegen, bevor Sie einen Operator in einer Befehlsliste aufzeichnen. Mit später Bindung können Sie Bindungen für Operatoren festlegen (oder ändern), die Sie bereits in einer Befehlsliste aufgezeichnet haben, bevor sie an die Befehlswarteschlange übermittelt wurde.
Rufen Sie IDMLDevice::CompileOperator mit einem flags
Argument von DML_EXECUTION_FLAG_DESCRIPTORS_VOLATILE auf, um eine späte Bindung anzugeben.